You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This provides two options for accessing the GUID value.
Get the guid value from user with the key guid.
Example
user=User(guid="foobar")
guid=user["guid"]
Get the guid value from user with the field guid.
Example
user=User(guid="foobar")
guid=user.guid
Backwards Compatibility
An implementation is backwards compatible when the implementation is written for a older version of Connect and continues operating correctly on a newer version of Connect which removes one or more fields.
This approach inherently supports backwards compatibility for the programmer under the following conditions:
The program does not access removed items by key.
The program accesses removed items by @property field.
The program is using the latest "minor" version of posit-sdk.
For developers, these constraints are met when:
The @property method implementation provides default behavior when the item is missing from the underlying dict.
To make these constraints clear to the programmer, the developer should utilize FutureWarning to notify the programmer of possible side-effects when performing key-based access patterns.
This can be accomplished by override the default behavior of all key-based dunder methods implemented by dict.
This includes the following methods:
__getitem__(self, key)
__setitem__(self, key, value)
__delitem__(self, key)
importwarningsclassResource(dict):
def__getitem__(self, key):
warnings.warn(f"__getitem__ for '{key}' does not support backwards compatibility. Consider using field based access instead: 'instance.{key}'", FutureWarning)
super().__getitem__(key)
def__setitem__(self, key, value):
warnings.warn(f"__setitem__ for '{key}' does not support backwards compatibility. Consider using field based access instead: 'instance.{key} = {value}", FutureWarning)
super().__getitem__(key, value)
def__delitem__(self, key):
warnings.warn(f"__delitem__ for '{key}' does not support backwards compatibility. Consider using field based access instead: 'del instance.{key}'", FutureWarning)
super().__delitem__(key)
This behavior may be disable by ignoring FutureWarning messages at runtime.
The major caveat to this approach is that pandas does not inspect @property fields when creating a DataFrame.
Therefore, the default behavior when initializing a pandas.DataFrame with a list[Resource] argument will result in a DataFrame with all underlying dict items as cells.
This behavior can be bypassed by converting the list[Resource] before initializing the pandas.DataFrame.
For example, the following snippet provides a workaround to this behavior. This comprehension creates a new dict using the property fields as the keys. In the future, this method or could be added to the SDK to support backwards compatible support for DataFrames.
An implementation is forwards compatible when the implementation is written for a older version of Connect and continues operating correctly on a newer version of Connect which adds one or more fields.
This approach inherently supports forwards compatibility since a Resource may be initialized with additional fields.
Alternatives
Data Classes
The dataclasses module provides a viable alternative, but introduces additional complexity that provides little value at this time.
The main benefit that dataclasses provides is the ability to define named types.
The drawback to dataclasses is that casting a dict to a dataclass an error will result if the dict contains keys not defined by the dataclass. This behavior can be modified by redefining init method and setting dataclass(init=False), but this implementation is non-obvious and will likely result in additional confusion for developers.
TypedDict
The TypedDict type suffers from the same drawbacks as @dataclass.
The default behavior of TypedDict does not allow additional kwargs to be passed on init. This behavior can be altered by setting total=False.
Additionally, TypedDict does not allow additional attributes. This forces backwards compatibility to be implemented outside of the Resource definition. This can be accomplished, but a successful implementation is tedious and error prone.
Overall, inheriting from TypedDict does not provide any major advantages over inheriting from dict.
The text was updated successfully, but these errors were encountered:
Discussed in #96
Originally posted by tdstein March 15, 2024
This discussion is a continuation of #92.
tl;dr - inherit from dict and provide
@property
fields for stable access patterns.Objective
Define a convention for
Resource
implementations that is intuitive to use and can provide consumers guarantees as the Connect API changes over time.Proposal
ABC
class namedResource
class that inherits fromdict
.Resource
(e.g.,class User(Resource)
)@property
annotation for fields that deserve long-time-support (LTS).Example Implementation:
This provides two options for accessing the GUID value.
guid
value fromuser
with the keyguid
.Example
guid
value fromuser
with the fieldguid
.Example
Backwards Compatibility
An implementation is backwards compatible when the implementation is written for a older version of Connect and continues operating correctly on a newer version of Connect which removes one or more fields.
This approach inherently supports backwards compatibility for the programmer under the following conditions:
@property
field.posit-sdk
.For developers, these constraints are met when:
@property
method implementation provides default behavior when the item is missing from the underlying dict.or
@property
method resolves to a new item when the original item is missing (i.e., a field name is changed).To make these constraints clear to the programmer, the developer should utilize
FutureWarning
to notify the programmer of possible side-effects when performing key-based access patterns.This can be accomplished by override the default behavior of all key-based dunder methods implemented by
dict
.This includes the following methods:
__getitem__(self, key)
__setitem__(self, key, value)
__delitem__(self, key)
This behavior may be disable by ignoring
FutureWarning
messages at runtime.Caveats
The major caveat to this approach is that
pandas
does not inspect@property
fields when creating aDataFrame
.Therefore, the default behavior when initializing a
pandas.DataFrame
with alist[Resource]
argument will result in aDataFrame
with all underlyingdict
items as cells.This behavior can be bypassed by converting the
list[Resource]
before initializing thepandas.DataFrame
.For example, the following snippet provides a workaround to this behavior. This comprehension creates a new
dict
using the property fields as the keys. In the future, this method or could be added to the SDK to support backwards compatible support for DataFrames.Forwards Compatibility
An implementation is forwards compatible when the implementation is written for a older version of Connect and continues operating correctly on a newer version of Connect which adds one or more fields.
This approach inherently supports forwards compatibility since a
Resource
may be initialized with additional fields.Alternatives
Data Classes
The
dataclasses
module provides a viable alternative, but introduces additional complexity that provides little value at this time.The main benefit that
dataclasses
provides is the ability to define named types.The other advantage to
dataclasses
is that conversion to apandas.DataFrame
is supported by pandas: https://github.com/pandas-dev/pandas/blob/v2.2.1/pandas/core/frame.py#L835The drawback to
dataclasses
is that casting adict
to adataclass
an error will result if thedict
contains keys not defined by thedataclass
. This behavior can be modified by redefining init method and settingdataclass(init=False)
, but this implementation is non-obvious and will likely result in additional confusion for developers.TypedDict
The
TypedDict
type suffers from the same drawbacks as@dataclass
.The default behavior of
TypedDict
does not allow additionalkwargs
to be passed on init. This behavior can be altered by settingtotal=False
.Additionally,
TypedDict
does not allow additional attributes. This forces backwards compatibility to be implemented outside of the Resource definition. This can be accomplished, but a successful implementation is tedious and error prone.Overall, inheriting from TypedDict does not provide any major advantages over inheriting from dict.
The text was updated successfully, but these errors were encountered: