Warning
Characteristic is unmaintained. Please have a look at its successor attrs.
Please use attrs instead!
Home Page: https://attrs.readthedocs.io/
License: MIT License
Warning
Characteristic is unmaintained. Please have a look at its successor attrs.
with_init
doesn't currently seem to support positional arguments. Maybe that's deliberate, but it's not explained in the documentation and the error message isn't particularly helpful.
I personally prefer keyword arguments, because it makes the code more readable, but sometimes the name of the variable is clue enough eg
In [13]: from characteristic import attributes
In [14]: class Foo(object):
pass
....:
In [15]: Foo = attributes(['reactor','url'])(Foo)
In [16]: dummyReactor = object()
In [17]: url = b'http://www.example.com'
In [18]: Foo(dummyReactor, url)
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
<ipython-input-18-181d7803e7c2> in <module>()
----> 1 Foo(dummyReactor, url)
/home/richard/.virtualenvs/flocker-30/lib/python2.7/site-packages/characteristic.pyc in init(self, *args, **kw)
124 v = defaults[a]
125 except KeyError:
--> 126 raise ValueError("Missing value for '{0}'.".format(a))
127 setattr(self, a, v)
128 self.__original_init__(*args, **kw)
ValueError: Missing value for 'reactor'.
So I propose adding support for positional arguments, but if not, add some better error handling and some documentation.
If I do this:
from characteristic import attributes
@attributes(["x"])
class X(object):
pass
X(x=1, y=2)
I get this error:
Traceback (most recent call last):
File "c.py", line 8, in <module>
X(x=1, y=2)
File ".../characteristic.py", line 128, in init
self.__original_init__(*args, **kw)
TypeError: object.__init__() takes no parameters
This isn't a great way of saying parameter "y" not expected
.
Quite often I want to define equality on objects by all or most of the attributes, but ordering by a different, smaller subset. Or, similarly, sometimes I want equality + hashing so that I can use them for set membership, but I don't want ordering, which is often meaningless.
Thoughts?
One of the nice things about namedtuple
is that it creates immutable objects. I very much like the approach that characteristic takes, but it'd be wonderful if it could be used to create immutable records.
One wants to unit test this sort of behavior but characteristic has enough information to automatically generate these tests in most cases.
Perhaps something like MyClassEqualityTests = make_equality_tests(MyClass)
?
Something along of instance_of=int
.
Needs a settled with_init
though #9.
It would be nice to able to say "these attributes are required" in with_init
and friends.
Hi, I'm getting tests failures on Arch Linux x86_64, with either Python 2.7.8 or 3.4.1.
Failures with Python 3.4.1:
============================= test session starts ==============================
platform linux -- Python 3.4.1 -- py-1.4.23 -- pytest-2.6.1
collected 61 items
test_characteristic.py ...........................................F........F........
=================================== FAILURES ===================================
____________________ TestWithInit.test_deprecation_defaults ____________________
self = <test_characteristic.TestWithInit object at 0x7fdb2307f978>
def test_deprecation_defaults(self):
"""
Emits a DeprecationWarning if `defaults` is used.
"""
with warnings.catch_warnings(record=True) as w:
@attributes(["a"], create_init=False)
class C(object):
pass
> assert (
'`create_init` has been deprecated in 14.0, please use '
'`apply_with_init`.'
) == w[0].message.args[0]
E IndexError: list index out of range
test_characteristic.py:480: IndexError
_________________ TestAttributes.test_deprecation_create_init __________________
self = <test_characteristic.TestAttributes object at 0x7fdb23718cf8>
def test_deprecation_create_init(self):
"""
Emits a DeprecationWarning if `create_init` is used.
"""
with warnings.catch_warnings(record=True) as w:
@attributes(["a"], create_init=False)
class C(object):
pass
> assert (
'`create_init` has been deprecated in 14.0, please use '
'`apply_with_init`.'
) == w[0].message.args[0]
E IndexError: list index out of range
test_characteristic.py:595: IndexError
===================== 2 failed, 59 passed in 0.13 seconds ======================
With Python 2.7.8:
============================= test session starts ==============================
platform linux2 -- Python 2.7.8 -- py-1.4.23 -- pytest-2.6.1
collected 61 items
test_characteristic.py ...........................................F........F........
=================================== FAILURES ===================================
____________________ TestWithInit.test_deprecation_defaults ____________________
self = <test_characteristic.TestWithInit object at 0x7f05c8345690>
def test_deprecation_defaults(self):
"""
Emits a DeprecationWarning if `defaults` is used.
"""
with warnings.catch_warnings(record=True) as w:
@attributes(["a"], create_init=False)
class C(object):
pass
> assert (
'`create_init` has been deprecated in 14.0, please use '
'`apply_with_init`.'
) == w[0].message.args[0]
E IndexError: list index out of range
test_characteristic.py:480: IndexError
_________________ TestAttributes.test_deprecation_create_init __________________
self = <test_characteristic.TestAttributes object at 0x7f05c80e4dd0>
def test_deprecation_create_init(self):
"""
Emits a DeprecationWarning if `create_init` is used.
"""
with warnings.catch_warnings(record=True) as w:
@attributes(["a"], create_init=False)
class C(object):
pass
> assert (
'`create_init` has been deprecated in 14.0, please use '
'`apply_with_init`.'
) == w[0].message.args[0]
E IndexError: list index out of range
test_characteristic.py:595: IndexError
===================== 2 failed, 59 passed in 0.11 seconds ======================
So, since we have .characteristic_attributes
, it's now possible to get the characteristic attributes of a characteristically-defined class. Hooray. But getting their values is very slightly nontrivial.
E.g., a few times I've written:
@attributes(
[...]
)
class Foo(object):
pass
attributes = {attr.name : getattr(foo_instance) for attr in foo_instance.characteristics}
new_foo = Foo(**attributes)
but this is subtly wrong, because you need to actually also call init_aliaser
, which only possibly shows up once you add a private attribute if you're using the default implementation.
Do you think it's worthwhile adding an API for this? Two ideas for one would be to add characteristic.characteristic_vars
to replace that dict comprehension, in which case the new way to do this would be Foo(**characteristic.characteristic_vars(...))
. Alternatively, another idea would be to just add a classmethod to characteristic classes to construct them out of dictionaries produced by characteristic_attributes. Either of these sound like decent ideas to you?
I have run into this use case a few times now: I want all of characteristic's goodness for the entire set of attributes, but it only makes sense for .hash() to reference some subset of of them, because the rest are not meaningful in that context. Characteristic already has a facility to make this distinction for compares (with_cmp/exclude_from_cmp) and I'd like to submit a PR that does it for hashes as well in an analogous manner.
Any objections @hynek ? ๐
This is particular useful for classes with a single attribute. See for example
https://github.com/radix/effect/blob/0.1a11/effect/__init__.py#L242-249
this is something i run into when developing all the time,
having a TypeError when its not a collection might be really helpfull
https://characteristic.readthedocs.org/en/stable/ has an "Edit on GitHub" link in the top right, but this goes to e.g. https://github.com/hynek/characteristic/blob/stable/docs/index.rst for the home page, which gives a 404.
Trying to use with_init
in both a base class and a derived class fails. It appears that the generated base class' __init__
is getting called twice. It looks like this is due to a confusion with both classes trying to control __original_init__
.
from characteristic import attributes
@attributes(["base"])
def Base(Exception):
pass
@attributes(["derived"])
def Derived(Base):
pass
Derived(base="base", derived="dervied")
fails with
Traceback (most recent call last):
File "test.py", line 11, in <module>
Derived(base="base", derived="dervied")
TypeError: Derived() got an unexpected keyword argument 'base
Just normal build, install and test cycle used on building package from non-root account:
+ PYTHONPATH=/home/tkloczko/rpmbuild/BUILDROOT/python-characteristic-14.3.0-2.fc35.x86_64/usr/lib64/python3.8/site-packages:/home/tkloczko/rpmbuild/BUILDROOT/python-characteristic-14.3.0-2.fc35.x86_64/usr/lib/python3.8/site-packages
+ PYTHONDONTWRITEBYTECODE=1
+ /usr/bin/pytest -ra
=========================================================================== test session starts ============================================================================
platform linux -- Python 3.8.9, pytest-6.2.4, py-1.10.0, pluggy-0.13.1
benchmark: 3.4.1 (defaults: timer=time.perf_counter disable_gc=False min_rounds=5 min_time=0.000005 max_time=1.0 calibration_precision=10 warmup=False warmup_iterations=100000)
rootdir: /home/tkloczko/rpmbuild/BUILD/characteristic-14.3.0
plugins: forked-1.3.0, shutil-1.7.0, virtualenv-1.7.0, expect-1.1.0, httpbin-1.0.0, xdist-2.2.1, flake8-1.0.7, timeout-1.4.2, betamax-0.8.1, freezegun-0.4.2, case-1.5.3, isort-1.3.0, aspectlib-1.5.2, asyncio-0.15.1, toolbox-0.5, xprocess-0.17.1, aiohttp-0.3.0, checkdocs-2.7.0, mock-3.6.1, rerunfailures-9.1.1, requests-mock-1.9.3, cov-2.12.1, pyfakefs-4.5.0, cases-3.6.1, flaky-3.7.0, hypothesis-6.14.0, benchmark-3.4.1, Faker-8.8.0
collected 89 items
. . [ 1%]
test_characteristic.py ....................................................................................... [100%]
============================================================================= warnings summary =============================================================================
test_characteristic.py::TestWithInit::test_defaults
/home/tkloczko/rpmbuild/BUILD/characteristic-14.3.0/test_characteristic.py:395: DeprecationWarning: `defaults` has been deprecated in 14.0, please use the `Attribute` class instead.
@with_init(["a", "b"], defaults={"b": 2})
test_characteristic.py::TestWithInit::test_defaults_conflict
/home/tkloczko/rpmbuild/BUILD/characteristic-14.3.0/test_characteristic.py:414: DeprecationWarning: `defaults` has been deprecated in 14.0, please use the `Attribute` class instead.
@with_init([Attribute("a")], defaults={"a": 42})
test_characteristic.py::TestAttributes::test_leaves_init_alone
/home/tkloczko/rpmbuild/BUILD/characteristic-14.3.0/test_characteristic.py:586: DeprecationWarning: `create_init` has been deprecated in 14.0, please use `apply_with_init`.
@attributes(["a"], create_init=False)
test_characteristic.py::TestEnsureAttributes::test_leaves_attribute_alone
test_characteristic.py::TestEnsureAttributes::test_converts_rest
test_characteristic.py::TestEnsureAttributes::test_defaults
test_characteristic.py::TestEnsureAttributes::test_defaults_Attribute
/usr/lib/python3.8/site-packages/_pytest/python.py:183: DeprecationWarning: `defaults` has been deprecated in 14.0, please use the `Attribute` class instead.
result = testfunction(**testargs)
-- Docs: https://docs.pytest.org/en/stable/warnings.html
====================================================================== 88 passed, 7 warnings in 7.18s ======================================================================
Hi, I have written a very basic .pyi
type stub for the python/typeshed repo. Permission from the package owner is required before the stub can be merged. Can you please provide it?
One of the primary benefits I get from having types is that my editor can help suggest appropriate completions and let me know when I'm doing something stupid.
But my editor currently doesn't understand that a class decorated with @attributes
has a constructor that takes those named parameters.
What would it take for a python editor to be able to recognize Characteristic attributes?
Could it be done with static analysis, or would it be easier to find out by some run-time introspection?
Would it require code specific to Characteristic, or could it be done with more general python introspection and/or type annotations?
In Twisted we try hard to limit the public API in new classes so it would be nice if with_init
had some way to map an argument name to an alternative private attribute name.
In fact it might even be enough to have a rule that argument names beginning with _
would be stored as private attributes but supplied without the _
prefix.
twisted.python.util.FancyStrMixin
has a horrible system where you can supply a tuple of (attr_name
, display_name
). I suppose you could do something like that here.
Or add a new Attribute
class with an argument_name
option...and then allow that to be supplied to the characteristic decorators...if you see what I mean.
It'd be nice if there were a way to retrieve the names of the attributes passed to @attributes
off the class. This allows things like being able to write:
def to_json(self):
return dict((attr, getattr(self, attr)) for attr in self.characteristic_attributes)
I wanted to replace a use of epsilon.structlike.record
with characteristic
. The original use looks like this:
class Link(record("source target")):
def __init__(self, *a, **k):
super(Link, self).__init__(*a, **k)
self.annotations = []
I started typing and got this far before I realized my mistake:
@attributes(["source", "target", "annotations"], {"annotations": []})
class Link(object):
I'll probably still use characteristic but I'm slightly sad to not be able to hoist the initialization of annotations
(and therefore be able to skip writing an __init__
).
It would be neat if characteristic offered support for this use.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.