-
Notifications
You must be signed in to change notification settings - Fork 15
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
introduce @field(disabled=True) #120
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,6 +16,10 @@ | |
_FIELDS_INFO_ATTRIBUTE_WRITE = "_web_poet_fields_info_temp" | ||
|
||
|
||
def _fields_template(): | ||
return {"enabled": {}, "disabled": {}} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. An alternative approach is to declare something like:
But I think it could be unwieldly when more field-flags are introduced later on. We could also simply use the same arrangement as before but then |
||
|
||
|
||
@attrs.define | ||
class FieldInfo: | ||
"""Information about a field""" | ||
|
@@ -29,6 +33,9 @@ class FieldInfo: | |
#: field processors | ||
out: Optional[List[Callable]] = None | ||
|
||
#: when set to ``True``, the field is not populated on ``.to_item()`` calls. | ||
disabled: bool = False | ||
|
||
|
||
class FieldsMixin: | ||
"""A mixin which is required for a class to support fields""" | ||
|
@@ -39,11 +46,31 @@ def __init_subclass__(cls, **kwargs): | |
# between subclasses, i.e. a decorator in a subclass doesn't affect | ||
# the base class. This is done by making decorator write to a | ||
# temporary location, and then merging it all on subclass creation. | ||
this_class_fields = getattr(cls, _FIELDS_INFO_ATTRIBUTE_WRITE, {}) | ||
base_class_fields = getattr(cls, _FIELDS_INFO_ATTRIBUTE_READ, {}) | ||
this_class_fields = getattr( | ||
cls, _FIELDS_INFO_ATTRIBUTE_WRITE, _fields_template() | ||
) | ||
base_class_fields = getattr( | ||
cls, _FIELDS_INFO_ATTRIBUTE_READ, _fields_template() | ||
) | ||
if base_class_fields or this_class_fields: | ||
fields = {**base_class_fields, **this_class_fields} | ||
setattr(cls, _FIELDS_INFO_ATTRIBUTE_READ, fields) | ||
enabled = {**base_class_fields["enabled"], **this_class_fields["enabled"]} | ||
for name in this_class_fields["disabled"]: | ||
if name in enabled: | ||
del enabled[name] | ||
|
||
disabled = { | ||
**base_class_fields["disabled"], | ||
**this_class_fields["disabled"], | ||
} | ||
for name in base_class_fields["disabled"]: | ||
if name in enabled: | ||
del disabled[name] | ||
|
||
setattr( | ||
cls, | ||
_FIELDS_INFO_ATTRIBUTE_READ, | ||
{"enabled": enabled, "disabled": disabled}, | ||
) | ||
with suppress(AttributeError): | ||
delattr(cls, _FIELDS_INFO_ATTRIBUTE_WRITE) | ||
|
||
|
@@ -54,6 +81,7 @@ def field( | |
cached: bool = False, | ||
meta: Optional[dict] = None, | ||
out: Optional[List[Callable]] = None, | ||
disabled: bool = False, | ||
): | ||
""" | ||
Page Object method decorated with ``@field`` decorator becomes a property, | ||
|
@@ -85,10 +113,11 @@ def __init__(self, method): | |
|
||
def __set_name__(self, owner, name): | ||
if not hasattr(owner, _FIELDS_INFO_ATTRIBUTE_WRITE): | ||
setattr(owner, _FIELDS_INFO_ATTRIBUTE_WRITE, {}) | ||
setattr(owner, _FIELDS_INFO_ATTRIBUTE_WRITE, _fields_template()) | ||
|
||
field_info = FieldInfo(name=name, meta=meta, out=out) | ||
getattr(owner, _FIELDS_INFO_ATTRIBUTE_WRITE)[name] = field_info | ||
field_info = FieldInfo(name=name, meta=meta, out=out, disabled=disabled) | ||
switch = "disabled" if disabled else "enabled" | ||
getattr(owner, _FIELDS_INFO_ATTRIBUTE_WRITE)[switch][name] = field_info | ||
|
||
def __get__(self, instance, owner=None): | ||
return self.unbound_method(instance) | ||
|
@@ -125,12 +154,21 @@ def processed(*args, **kwargs): | |
return _field | ||
|
||
|
||
def get_fields_dict(cls_or_instance) -> Dict[str, FieldInfo]: | ||
def get_fields_dict( | ||
cls_or_instance, include_disabled: bool = False | ||
) -> Dict[str, FieldInfo]: | ||
"""Return a dictionary with information about the fields defined | ||
for the class: keys are field names, and values are | ||
:class:`web_poet.fields.FieldInfo` instances. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. TODO: update this docstring when |
||
""" | ||
return getattr(cls_or_instance, _FIELDS_INFO_ATTRIBUTE_READ, {}) | ||
fields_info = getattr( | ||
cls_or_instance, _FIELDS_INFO_ATTRIBUTE_READ, _fields_template() | ||
) | ||
fields_dict = {} | ||
fields_dict.update(fields_info["enabled"]) | ||
if include_disabled: | ||
fields_dict.update(fields_info["disabled"]) | ||
return fields_dict | ||
|
||
|
||
T = TypeVar("T") | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe
enabled
would be a better name.I would prefer something that suggests how the field can still be used as a property but is not included in the
to_item
output, but I cannot think of a good name (export
?itemize
?itemizable
?to_item
?add_to_item
?include_in_item
?).There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My initial implementation actually used
enabled
, but it seemsget_fields_dict(include_disabled=True)
sounds much better.Hmmm,
to_item
andinclude_in_item
does sound better in terms of what it does. Currently, eitherenabled
ordisabled
isn't that clear about what it does.+1 on something like
@field(to_item=False)
.Alongside with this, perhaps renaming to
get_fields_dict(all_fields=True)
would work.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was initially thinking about something like "exclude_by_default=True" or "excluded_by_default=True", or "extract_by_default=False"; alternatives like "to_item=False" also look fine.
A possible issue with to_item=False is that you may think such field doesn't have to be defined in the item, but it does; I think it's still an error if item doesn't have it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think these have the same issue with
include
andexclude
where it's not clear what's happening, i.e., where it's being excluded from.You're right. What do you think about having something like
omit_in_to_item=True
orto_item_omit=True
?