Skip to content

Add attrs library #1838

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

Merged
merged 5 commits into from
Feb 3, 2018
Merged

Add attrs library #1838

merged 5 commits into from
Feb 3, 2018

Conversation

chadrik
Copy link
Contributor

@chadrik chadrik commented Jan 21, 2018

Hi, here are stubs for attrs. I've already received permission from @hynek here.

After this is merged, @euresti will follow up with his PR on mypy to add plugin support.

Copy link
Member

@ilevkivskyi ilevkivskyi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just two style comments.


# _make --

NOTHING : object
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PEP 8 requires no space before colon.


# 1st form catches _T set.
@overload
def attrib(default: Optional[_T] = ..., validator: Optional[_ValidatorArgType[_T]] = ..., repr: bool = ..., cmp: bool = ..., hash: Optional[bool] = ..., init: bool = ..., metadata: Mapping = ..., type: Optional[Type[_T]] = ..., converter: Optional[_ConverterType[_T]] = ...) -> _T: ...
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can split this definition one parameter per line:

def fun(x: type_x,
        y: type_y,
        z: type_z) -> ret_type: ...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can split this definition one parameter per line:

I'm happy to oblige, but I'm having a little trouble reconciling that advice with the guidelines in CONTRIBUTING.md:

  • there is no line length limit;
  • prefer long lines over elaborate indentation;

If there is no line length limit, how do I decide which definitions should be split per line? This also seems to go against the latter, preferring long lines to indentation.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are no strict rules, you should just apply your reason, as an example there is builtins.open in Python 3.
My rule of thumb is if it doesn't fit in one line on my Mac, then it is definitely too long :-)

@chadrik
Copy link
Contributor Author

chadrik commented Jan 26, 2018

@ilevkivskyi What's next?

@ilevkivskyi
Copy link
Member

The mypy PR you mentioned is higher priority for me than this (and Python 3.7 beta 1 in four days is even higher priority).

@chadrik
Copy link
Contributor Author

chadrik commented Jan 26, 2018

@euresti, who is doing the mypy PR, has indicated that he needs this merged to get his PR passing tests and mergeable.

@euresti
Copy link
Contributor

euresti commented Jan 26, 2018

Technically, without the stubs the plugin would never be called. (I haven't looked into why I just tested it and it doesn't get called.)

@ilevkivskyi
Copy link
Member

Technically, without the stubs the plugin would never be called.

This is because mypy currently only uses only typeshed. After PEP 561 is implemented it will also accept inline annotations and side-by-side stubs in third party packages.

Copy link
Member

@JelleZijlstra JelleZijlstra left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't have time to review all this against the attrs implementation right now, but left a few comments.

_ValidatorType = Callable[[Any, Attribute, _T], Any]
_ConverterType = Callable[[Any], _T]
_FilterType = Callable[[Attribute, Any], bool]
_ValidatorArgType = Union[_ValidatorType[_T], List[_ValidatorType[_T]], Tuple[_ValidatorType[_T], ...]]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't have time to read through the attrs code right now, but why isn't this just Iterable or Sequence? Does it really accept only lists and tuples?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code literally does isinstance(validator, (list, tuple)):

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's unfortunate because list and tuple are invariant, so subtypes of _ValidatorType may end up not getting accepted.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. Confirmed that I'm getting errors with lists of validators. I'm going to use Sequence for now and I'll open a separate ticket against attrs to see if this can be addressed in the code.

cmp: bool = ...,
hash: Optional[bool] = ...,
init: bool = ...,
metadata: Mapping = ...,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing type arguments to Mapping.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

attrs doesn't have any opinion or requirement about the types within the mapping.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should probably be Optional[Mapping[Any, Any]] since None is acceptable

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In general, even if the type arguments are Any I prefer being explicit, so that we can detect cases where we skip type arguments by accident.

def get_run_validators() -> bool: ...

# aliases --
# s = attributes = attrs
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why can't you just write these lines instead of duplicating the definitions? Does that interact poorly with overloads?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just below those commented lines:

# FIXME: there is a bug in PyCharm with creating aliases to overloads.
# Remove these when the bug is fixed:
# https://youtrack.jetbrains.com/issue/PY-27788

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would take out the commented out assignments entirely. Just to avoid confusion.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I find it's nice when testing to be able to uncomment them. I'll move the FIXME to a more co-located place.

Copy link
Member

@JelleZijlstra JelleZijlstra left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few more minor comments, then this should be good to go.

_ValidatorType = Callable[[Any, Attribute, _T], Any]
_ConverterType = Callable[[Any], _T]
_FilterType = Callable[[Attribute, Any], bool]
_ValidatorArgType = Union[_ValidatorType[_T], List[_ValidatorType[_T]], Tuple[_ValidatorType[_T], ...]]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's unfortunate because list and tuple are invariant, so subtypes of _ValidatorType may end up not getting accepted.

cmp: bool = ...,
hash: Optional[bool] = ...,
init: bool = ...,
metadata: Mapping = ...,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In general, even if the type arguments are Any I prefer being explicit, so that we can detect cases where we skip type arguments by accident.

init: bool = ...,
metadata: Mapping = ...,
type: Optional[Type[_T]] = ...,
converter: Optional[_ConverterType[_T]] = ...) -> _T: ...
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The convert argument is missing. It is apparently deprecated, but we should still include it because otherwise the positional arguments are out of sync. We could perhaps mark some or all of the arguments as keyword-only here (it will work even in Python 2).


# TODO: add support for returning a proper attrs class from the mypy plugin
# we use Any instead of _CountingAttr so that e.g. `make_class('Foo', [attr.ib()])` is valid
def make_class(name, attrs: Union[List[str], Dict[str, Any]], bases: Tuple[type, ...] = ..., **attributes_arguments) -> type: ...
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing type (presumably Any) on attributes_arguments

- add attrib(convert)
- replace Mapping with Optional[Mapping[Any, Any]]
- add make_class arg types
- deal properly with sequences of validator types
- fix a bug with forward references
@chadrik
Copy link
Contributor Author

chadrik commented Feb 2, 2018

review notes addressed.

Btw, I've added pretty thorough tests to my attrs PR for pyi stubs which cover both the attrs stubs and mypy plugin.

@JelleZijlstra
Copy link
Member

Thanks for addressing the comments! There's a minor lint error now.

@JelleZijlstra JelleZijlstra merged commit b111a45 into python:master Feb 3, 2018
@gvanrossum
Copy link
Member

gvanrossum commented Feb 3, 2018 via email

rhysparry added a commit to rhysparry/typeshed that referenced this pull request Feb 8, 2018
* python/master: (269 commits)
  allow Optional[float] for asyncio.wait() timeout arg (python#1860)
  Clean up the pytype blacklist. (python#1851)
  filter function: make the callable of the first overload non-optional so that mypy is able to select the second overload for the  case. (python#1855)
  Accept Text in distutils *Version classes (python#1854)
  Add attrs library (python#1838)
  Add type stub for the lzma module (python#1844)
  Add ImportError attributes name, path for Python 3.3+ (python#1849)
  Add 'message' to click.decorators.version_option (python#1845)
  PEP 553 and PEP 564 (python#1846)
  Adding py36 additions to datetime module (python#1848)
  Remove incomplete thrift stubs that cause false positives. (python#1827)
  adding curses.ascii, curses.panel and curses.textpad modules (python#1792)
  Fix requests session hooks type (python#1794)
  Add StringIO.name and BytesIO.name (python#1802)
  Fix werkzeug environ type (python#1831)
  Change the return type of unittest.TestCase.fail() to NoReturn (python#1843)
  _curses.tparm works on bytes, not str (python#1828)
  Refine types for distutils.version (python#1835)
  Add stub for stat.filemode (python#1837)
  modify pytype_test to run from within pytype too, and support python3 (python#1817)
  ...
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants