Git Product home page Git Product logo

smarie / python-autoclass Goto Github PK

View Code? Open in Web Editor NEW
36.0 4.0 2.0 2.92 MB

A python 3 library providing functions and decorators to automatically generate class code, such as constructor body or properties getters/setters along with optional support of validation contracts on the generated setters. Its objective is to reduce the amount of copy/paste code in your classes - and hence to help reducing human mistakes :).

Home Page: https://smarie.github.io/python-autoclass/

License: BSD 3-Clause "New" or "Revised" License

Python 99.58% Shell 0.42%
code-generator constructor autoargs autoprops contracts enforce boilerplate pep484 type-hints runtime-typechecking

python-autoclass's Introduction

python-autoclass

Write compact python classes

Python versions Build Status Tests Status codecov

Documentation PyPI Downloads Downloads per week GitHub stars

This is the readme for developers. The documentation for users is available here: https://smarie.github.io/python-autoclass/

Want to contribute ?

Contributions are welcome ! Simply fork this project on github, commit your contributions, and create pull requests.

Here is a non-exhaustive list of interesting open topics: https://github.com/smarie/python-autoclass/issues

Running the tests

This project uses pytest.

pytest -v autoclass/tests/

You may need to install requirements for setup beforehand, using

pip install -r ci_tools/requirements-test.txt

Packaging

This project uses setuptools_scm to synchronise the version number. Therefore the following command should be used for development snapshots as well as official releases:

python setup.py egg_info bdist_wheel rotate -m.whl -k3

You may need to install requirements for setup beforehand, using

pip install -r ci_tools/requirements-setup.txt

Generating the documentation page

This project uses mkdocs to generate its documentation page. Therefore building a local copy of the doc page may be done using:

mkdocs build -f docs/mkdocs.yml

You may need to install requirements for doc beforehand, using

pip install -r ci_tools/requirements-doc.txt

Generating the test reports

The following commands generate the html test report and the associated badge.

pytest --junitxml=junit.xml -v autoclass/tests/
ant -f ci_tools/generate-junit-html.xml
python ci_tools/generate-junit-badge.py

PyPI Releasing memo

This project is now automatically deployed to PyPI when a tag is created. Anyway, for manual deployment we can use:

twine upload dist/* -r pypitest
twine upload dist/*

Merging pull requests with edits - memo

Ax explained in github ('get commandline instructions'):

git checkout -b <git_name>-<feature_branch> master
git pull https://github.com/<git_name>/python-autoclass.git <feature_branch> --no-commit --ff-only

if the second step does not work, do a normal auto-merge (do not use rebase!):

git pull https://github.com/<git_name>/python-autoclass.git <feature_branch> --no-commit

Finally review the changes, possibly perform some modifications, and commit.

python-autoclass's People

Contributors

smarie avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

python-autoclass's Issues

Setter is called twice for default values

When an argument is declared as optional with default value in the constructor, everytime it is set by @autoargs the setter is called twice (once with the default value and once with the value provided by the user)

global counter
counter = 0

class Home(object):
    @autoargs
    def __init__(self, foo, bar=False):
        pass

    @property
    def bar(self):
        return self._bar

    @bar.setter
    def bar(self, value):
        global counter
        counter += 1
        self._bar = value

Home(None, bar=True)
assert counter == 1  # AssertionError : counter is 2 !

Improve default string representation in `@autodict`

By default @autodict creates a string representation that is not very user-friendly (House({'name': 'mine', 'nb_floors': 100})).

Let's propose by default a better representation : House(name='mine', nb_floors=100)

Of course we should provide an option to fallback to legacy behaviour if users need it

Enforce + Autoclass: AttributeError: 'functools.partial' object has no attribute '__enforcer__'

This works:

from autoclass import autoclass, setter_override
from numbers import Real
from enforce import runtime_validation, config
config(dict(mode='covariant'))  # to accept subclasses in validation

@runtime_validation
@autoclass
class HouseConfiguration(object):
    def __init__(self, surface: Real):
        pass

    # -- overriden setter for surface
    @setter_override
    def surface(self, surface):
        print('Set surface to {}'.format(surface))
        self._surface = surface

t = HouseConfiguration(12)

While this throws an exception AttributeError: 'functools.partial' object has no attribute '__enforcer__':

from autoclass import autoclass, setter_override
from numbers import Real
from enforce import runtime_validation, config
config(dict(mode='covariant'))  # to accept subclasses in validation

@autoclass
@runtime_validation
class HouseConfiguration(object):
    def __init__(self, surface: Real):
        pass

    # -- overriden setter for surface
    @setter_override
    def surface(self, surface):
        print('Set surface to {}'.format(surface))
        self._surface = surface

t = HouseConfiguration(12)

The only difference is the order of the annotations on class HouseConfiguration.

Type error when using the generated `from_dict` method with pytypes

When using pytypes type checker with the from_dict method generated by @autoclass/@autodict:

from pytypes import typechecked
from autoclass import autoclass

@typechecked
@autoclass
class Foo:
    def __init__(self, foo):
         pass

Foo.from_dict(dict(foo=1))

returns

pytypes.exceptions.InputTypeError: 
  classmethod autoclass.autodict.Foo.from_dict
  called with incompatible types:
Expected: Tuple[Dict]
Received: Tuple[Dict[str, int]]

Can python-autoclass be used with dataclasses/datafiles?

I'm looking at datafiles

I like the ORM mapping of yaml that dataclasses/datafiles provides but I also love the features that python-autoclass provides, however, I can't tell if they can be used together, if they would be redundant, complementary, or if one would interfere with the other. If they can work together, would the order that the annotations are specified matter?

Edit: changed link to the actual module I would like to use.

Improve __init__.py

It seems that all should also contain the names of all symbols exported by each module. Let's try to automatically generate this from public names.

Besides, it seems that the symbols imported by the package are re-exported (or at least pycharm thinks so). For example typing symbols, etc. Let's dig this topic too...

@autohash

This would generate a def __hash__ based on the tuple of included/excluded attributes

`@autorepr`

As of now string representation is done by @autodict and relies on the .items() view to create the representation. It therefore can not be used independently. It would be good to propose an independent decorator for this.

Inheritance of @autoclass tagged classes (@autodict inheritance issues)

As of today inheritance is not intuitive:

from autoclass import autoclass

@autoclass
class Foo:
    def __init__(self, foo):
        pass

@autoclass
class Bar(Foo):
    def __init__(self, bar):
        pass

yields

> ValueError: @autodict can not be set on classes that are already subclasses of Mapping, and therefore already behave like dict

In order for this to work, you have to use @autoclass(autodict=False) in the parent class. But this is not user-friendly. The proposal would be to remove this exception, and make all dict implemented methods call the corresponding super() method where appropriate

independent @autoeq with different behaviour than the one included in @autodict

Currently, only @autodict (or @autoclass with default option autodict=True) generates a __eq__ implementation, which is based on dictionary equality. It would be better to

  • have an independent way of generating the equality method (@autoeq),
  • and to change @autodict behaviour: rather than implementing a dictionary directly on the object, it could instead provide a as_dict() method which would be that view.

error with python 3.5.2

Hi,

I'm using python 3.5.2
The import of autoclass.autoprops raise the following error:
(ps. I updated the module typing)

Traceback (most recent call last):
  File "C:/Users/~/Desktop/test_autoclass.py", line 1, in <module>
    from autoclass import autoargs, autoprops, setter_override
  File "C:\Program Files\Anaconda3\lib\site-packages\autoclass\__init__.py", line 2, in <module>
    from autoclass.autoprops import *
  File "C:\Program Files\Anaconda3\lib\site-packages\autoclass\autoprops.py", line 169, in <module>
    def _get_getter_fun(object_type: Type, property_name: str, property_type: Optional[Type], private_property_name: str):
  File "C:\Program Files\Anaconda3\lib\typing.py", line 649, in __getitem__
    return Union[arg, type(None)]
  File "C:\Program Files\Anaconda3\lib\typing.py", line 552, in __getitem__
    dict(self.__dict__), parameters, _root=True)
  File "C:\Program Files\Anaconda3\lib\typing.py", line 512, in __new__
    for t2 in all_params - {t1} if not isinstance(t2, TypeVar)):
  File "C:\Program Files\Anaconda3\lib\typing.py", line 512, in <genexpr>
    for t2 in all_params - {t1} if not isinstance(t2, TypeVar)):
  File "C:\Program Files\Anaconda3\lib\typing.py", line 1077, in __subclasscheck__
    if super().__subclasscheck__(cls):
  File "C:\Program Files\Anaconda3\lib\abc.py", line 225, in __subclasscheck__
    for scls in cls.__subclasses__():
TypeError: descriptor '__subclasses__' of 'type' object needs an argument

@autoprops: Add default value annotation in setters for optional constructor arguments

Let's consider type checking with enforce with implicit optional arguments. Both of these statements succeed.

from enforce import runtime_validation

@runtime_validation
def foo(mandatory_arg: str, optional_arg: str = None):
    pass
foo('t')

@runtime_validation
class Foo:
    def __init__(self, mandatory_field: str, optional_field: str = None):
        pass
Foo('t')

However, the following fails in autoclass:

@runtime_validation
@autoclass
class Foo:
    def __init__(self, mandatory_field: str, optional_field: str = None):
        pass
a = Foo('t')
a.optional_field = None  # raises RuntimeTypeError: Argument 'val' was not of type <class 'str'>. Actual type was NoneType.

This is due to the fact that

  • the PEP484 notation for Optionality has not been explicitly used (no Optional[str]), so optionality is implicitly detected by checking if there is a None default value
  • however in the property setter generated by @autoprops there is no such default value >> None is therefore not considered valid

Fixing this is relatively easy: @autoprops should correctly set the __defaults__ annotation on the generated property setter.

Wrong function name in validation error message of field setter

from autoclass import autoclass
from pytypes import typechecked
from valid8 import validate_arg, is_multiple_of
from mini_lambda import s, x, Len

@typechecked
@autoclass
class House:
    @validate_arg('name', Len(s) > 0)
    @validate_arg('surface', (x >= 0) & (x < 10000), is_multiple_of(100))
    def __init__(self, name: str, surface: int = 100):
        pass

If we create an object and then set a wrong value for the field, the exception contains a wrong reference to __init__ function:

o = House('helo')
o.surface = 150

KeyError: '@autodict generated dict view - args is a constructor parameter but is not a field (was the constructor called ?)'

This happens when I create a simple class and use repr on the object:

from autoclass import autoclass

@autoclass
class City:
    name: Optional[str]
    buildings: List[str] = []
>>> City("mine")

yields

  File "C:\_dev\python_ws\_Libs_OpenSource\python-autoclass\autoclass\autodict_.py", line 456, in __getitem__
    'field (was the constructor called ?)'.format(key))
KeyError: '@autodict generated dict view - args is a constructor parameter but is not a field (was the constructor called ?)'

@autoprops argument name in setter is not correct

The setter function generated by @autoprops has always the same argument name : 'val'.
This was primarily done so that there is no dynamic compilation, but it leads to confusing error messages when used in combination with PEP484 type checkers:

from autoclass import autoclass
from numbers import Integral

# we use enforce runtime checker for this example
from enforce import runtime_validation, config
config(dict(mode='covariant'))  # to accept subclasses in validation

@runtime_validation
@autoclass
class House:
    def __init__(self, name: str, nb_floors: Integral = 1):
        pass

obj = House('my_house', 'red')

Leads to the following error message:

enforce.exceptions.RuntimeTypeError: 
  The following runtime type errors were encountered:
       Argument 'val' was not of type <class 'numbers.Integral'>. Actual type was str.

The argument name is val in the error message, not nb_floors.

autodict alternative generating a `to_dict()` or `as_dict()` method

@autodict currently makes the class behave as a dict so that all atributes can also be accessed as dict items.

  • side remark: current implementation is maybe (?) overkill for non-slots classes where there is already a __dict__ inside. This is not really an issue for now
  • some (most?) users might prefer to have a as_dict or to_dict method to convert the object

Support `pyfields` as an alternate way to define attributes

As of today, all decorators provided in autoclass source their initial attribute list on the constructor (__init__) signature.

Alternately one could wish to define the fields using pyfields:

  • @autodict and @autohash would work independently
  • @autoclass would also work: it would apply only @autodict and @autohash (and not @autoargs nor @autoprops as they are not anymore relevant in this context)
from autoclass import autoclass
from pyfields import field

@autoclass
class A(object):
    a = field()

Being able to customize all or a subset of generated getters and setters at once

New annotations @getters_wrapper(include, exclude) and @setters_wrapper(include, exclude), that would use @contextmanager or directly extend GeneratorContextManager in order to help users add functionality to all or some of the generated getters/setters with a function containing yield. For example in order to add a log message whenever properties are changed, the user would add a single log_property_change method to the class, decorated with @setters_wrapper.

IDE Static attribute checking shows warning

@smarie Not sure if there is a way around this: In IDE (I'm using pyCharm), when using autoargs the class attribute shows up as undefined during the static checking of the IDE. It looks like this:

screenshot 2017-09-04 05 58 34

It would be great if there is a way to get around this issue.

Exclude argument does not work properly with autohash

Suppose I want to exclude a couple fields from the hash because for example they are not hashable.

@autoclass(autohash=False)
@autohash(exclude='bar')
class Foo:
    def __init__(self, foo: str, bar: Dict[str, str]):
        pass

The above does not work:

> a = Foo('hello', dict())
> hash(a)  # TypeError: unhashable type: 'dict'

The only workaround as of today is to use the private name associated with the attribute:

@autoclass(autohash=False)
@autohash(exclude='_bar')  # <- here use the private name
class Foo:
    def __init__(self, foo: str, bar: Dict[str, str]):
        pass

But it is not satisfying.

Note that there is probably a similar behaviour in @autodict with non-None exclude/include.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.