New Research: Supply Chain Attack on Axios Pulls Malicious Dependency from npm.Details →
Socket
Book a DemoSign in
Socket

dataclasses

Package Overview
Dependencies
Maintainers
1
Versions
8
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

dataclasses - pypi Package Compare versions

Comparing version
0.4
to
0.5
+1
-1
dataclasses.egg-info/PKG-INFO
Metadata-Version: 1.1
Name: dataclasses
Version: 0.4
Version: 0.5
Summary: An implementation of PEP 557: Data Classes

@@ -5,0 +5,0 @@ Home-page: https://github.com/ericvsmith/dataclasses

import sys
import copy
import types
from copy import deepcopy
import inspect

@@ -8,2 +8,3 @@

'field',
'Field',
'FrozenInstanceError',

@@ -24,7 +25,7 @@ 'InitVar',

# dataclass decorator takes. For all of these tables, when I talk
# about init=, repr=, eq=, order=, hash=, or frozen=, I'm referring
# to the arguments to the @dataclass decorator. When checking if a
# dunder method already exists, I mean check for an entry in the
# class's __dict__. I never check to see if an attribute is defined
# in a base class.
# about init=, repr=, eq=, order=, unsafe_hash=, or frozen=, I'm
# referring to the arguments to the @dataclass decorator. When
# checking if a dunder method already exists, I mean check for an
# entry in the class's __dict__. I never check to see if an
# attribute is defined in a base class.

@@ -39,7 +40,2 @@ # Key:

# +---------+-----------------------------------------+
# | add* | Generated method is added only if the |
# | | existing attribute is None and if the |
# | | user supplied a __eq__ method in the |
# | | class definition. |
# +---------+-----------------------------------------+
# | raise | TypeError is raised. |

@@ -121,39 +117,32 @@ # +---------+-----------------------------------------+

# +------------------- hash= parameter
# | +----------- eq= parameter
# | | +--- frozen= parameter
# | | |
# v v v | | |
# | no | yes | <--- class has __hash__ in __dict__?
# +=========+=======+=======+========+========+
# | 1 None | False | False | | | No __eq__, use the base class __hash__
# +---------+-------+-------+--------+--------+
# | 2 None | False | True | | | No __eq__, use the base class __hash__
# +---------+-------+-------+--------+--------+
# | 3 None | True | False | None | | <-- the default, not hashable
# +---------+-------+-------+--------+--------+
# | 4 None | True | True | add | add* | Frozen, so hashable
# +---------+-------+-------+--------+--------+
# | 5 False | False | False | | |
# +---------+-------+-------+--------+--------+
# | 6 False | False | True | | |
# +---------+-------+-------+--------+--------+
# | 7 False | True | False | | |
# +---------+-------+-------+--------+--------+
# | 8 False | True | True | | |
# +---------+-------+-------+--------+--------+
# | 9 True | False | False | add | add* | Has no __eq__, but hashable
# +---------+-------+-------+--------+--------+
# |10 True | False | True | add | add* | Has no __eq__, but hashable
# +---------+-------+-------+--------+--------+
# |11 True | True | False | add | add* | Not frozen, but hashable
# +---------+-------+-------+--------+--------+
# |12 True | True | True | add | add* | Frozen, so hashable
# +=========+=======+=======+========+========+
# +------------------- unsafe_hash= parameter
# | +----------- eq= parameter
# | | +--- frozen= parameter
# | | |
# v v v | | |
# | no | yes | <--- class has explicitly defined __hash__
# +=======+=======+=======+========+========+
# | False | False | False | | | No __eq__, use the base class __hash__
# +-------+-------+-------+--------+--------+
# | False | False | True | | | No __eq__, use the base class __hash__
# +-------+-------+-------+--------+--------+
# | False | True | False | None | | <-- the default, not hashable
# +-------+-------+-------+--------+--------+
# | False | True | True | add | | Frozen, so hashable, allows override
# +-------+-------+-------+--------+--------+
# | True | False | False | add | raise | Has no __eq__, but hashable
# +-------+-------+-------+--------+--------+
# | True | False | True | add | raise | Has no __eq__, but hashable
# +-------+-------+-------+--------+--------+
# | True | True | False | add | raise | Not frozen, but hashable
# +-------+-------+-------+--------+--------+
# | True | True | True | add | raise | Frozen, so hashable
# +=======+=======+=======+========+========+
# For boxes that are blank, __hash__ is untouched and therefore
# inherited from the base class. If the base is object, then
# id-based hashing is used.
# Note that a class may have already __hash__=None if it specified an
# Note that a class may already have __hash__=None if it specified an
# __eq__ method in the class body (not one that was created by
# @dataclass).
# See _hash_action (below) for a coded version of this table.

@@ -165,3 +154,3 @@

# A sentinel object for default values to signal that a
# default-factory will be used.
# default factory will be used.
# This is given a nice repr() which will appear in the function

@@ -191,4 +180,8 @@ # signature of dataclasses' constructors.

# objects. Also used to check if a class is a Data Class.
_MARKER = '__dataclass_fields__'
_FIELDS = '__dataclass_fields__'
# The name of an attribute on the class that stores the parameters to
# @dataclass.
_PARAMS = '__dataclass_params__'
# The name of the function, that if it exists, is called at the end of

@@ -213,3 +206,3 @@ # __init__.

# convenient if they're available later.
# When cls._MARKER is filled in with a list of Field objects, the name
# When cls._FIELDS is filled in with a list of Field objects, the name
# and type fields will have been populated.

@@ -257,3 +250,45 @@ class Field:

# This is used to support the PEP 487 __set_name__ protocol in the
# case where we're using a field that contains a descriptor as a
# defaul value. For details on __set_name__, see
# https://www.python.org/dev/peps/pep-0487/#implementation-details.
# Note that in _process_class, this Field object is overwritten with
# the default value, so the end result is a descriptor that had
# __set_name__ called on it at the right time.
def __set_name__(self, owner, name):
func = getattr(self.default, '__set_name__', None)
if func:
# There is a __set_name__ method on the descriptor,
# call it.
func(owner, name)
class _DataclassParams:
__slots__ = ('init',
'repr',
'eq',
'order',
'unsafe_hash',
'frozen',
)
def __init__(self, init, repr, eq, order, unsafe_hash, frozen):
self.init = init
self.repr = repr
self.eq = eq
self.order = order
self.unsafe_hash = unsafe_hash
self.frozen = frozen
def __repr__(self):
return ('_DataclassParams('
f'init={self.init},'
f'repr={self.repr},'
f'eq={self.eq},'
f'order={self.order},'
f'unsafe_hash={self.unsafe_hash},'
f'frozen={self.frozen}'
')')
# This function is used instead of exposing Field creation directly,

@@ -299,2 +334,4 @@ # so that a type checker can be told (via overloads) that this is a

# Note that we mutate locals when exec() is called. Caller beware!
# The only callers are internal to this module, so no worries
# about external callers.
if locals is None:

@@ -309,2 +346,3 @@ locals = {}

# Compute the text of the entire function.
txt = f'def {name}({args}){return_annotation}:\n{body}'

@@ -422,7 +460,6 @@

for f in fields:
# Do not initialize the pseudo-fields, only the real ones.
line = _field_init(f, frozen, globals, self_name)
if line is not None:
# line is None means that this field doesn't require
# initialization. Just skip it.
# line is None means that this field doesn't require
# initialization (it's a pseudo-field). Just skip it.
if line:
body_lines.append(line)

@@ -434,3 +471,3 @@

if f._field_type is _FIELD_INITVAR)
body_lines += [f'{self_name}.{_POST_INIT_NAME}({params_str})']
body_lines.append(f'{self_name}.{_POST_INIT_NAME}({params_str})')

@@ -443,3 +480,3 @@ # If no body lines, use 'pass'.

return _create_fn('__init__',
[self_name] +[_init_param(f) for f in fields if f.init],
[self_name] + [_init_param(f) for f in fields if f.init],
body_lines,

@@ -453,3 +490,3 @@ locals=locals,

return _create_fn('__repr__',
['self'],
('self',),
['return self.__class__.__qualname__ + f"(' +

@@ -461,10 +498,27 @@ ', '.join([f"{f.name}={{self.{f.name}!r}}"

def _frozen_setattr(self, name, value):
raise FrozenInstanceError(f'cannot assign to field {name!r}')
def _frozen_get_del_attr(cls, fields):
# XXX: globals is modified on the first call to _create_fn, then the
# modified version is used in the second call. Is this okay?
globals = {'cls': cls,
'FrozenInstanceError': FrozenInstanceError}
if fields:
fields_str = '(' + ','.join(repr(f.name) for f in fields) + ',)'
else:
# Special case for the zero-length tuple.
fields_str = '()'
return (_create_fn('__setattr__',
('self', 'name', 'value'),
(f'if type(self) is cls or name in {fields_str}:',
' raise FrozenInstanceError(f"cannot assign to field {name!r}")',
f'super(cls, self).__setattr__(name, value)'),
globals=globals),
_create_fn('__delattr__',
('self', 'name'),
(f'if type(self) is cls or name in {fields_str}:',
' raise FrozenInstanceError(f"cannot delete field {name!r}")',
f'super(cls, self).__delattr__(name)'),
globals=globals),
)
def _frozen_delattr(self, name):
raise FrozenInstanceError(f'cannot delete field {name!r}')
def _cmp_fn(name, op, self_tuple, other_tuple):

@@ -477,3 +531,3 @@ # Create a comparison function. If the fields in the object are

return _create_fn(name,
['self', 'other'],
('self', 'other'),
[ 'if other.__class__ is self.__class__:',

@@ -487,3 +541,3 @@ f' return {self_tuple}{op}{other_tuple}',

return _create_fn('__hash__',
['self'],
('self',),
[f'return hash({self_tuple})'])

@@ -493,7 +547,7 @@

def _get_field(cls, a_name, a_type):
# Return a Field object, for this field name and type. ClassVars
# Return a Field object for this field name and type. ClassVars
# and InitVars are also returned, but marked as such (see
# f._field_type).
# If the default value isn't derived from field, then it's
# If the default value isn't derived from Field, then it's
# only a normal default value. Convert it to a Field().

@@ -504,2 +558,5 @@ default = getattr(cls, a_name, MISSING)

else:
if isinstance(default, types.MemberDescriptorType):
# This is a field in __slots__, so it has no default value.
default = MISSING
f = field(default=default)

@@ -541,5 +598,5 @@

# Should I check for other field settings? default_factory
# seems the most serious to check for. Maybe add others. For
# example, how about init=False (or really,
# init=<not-the-default-init-value>)? It makes no sense for
# seems the most serious to check for. Maybe add others.
# For example, how about init=False (or really,
# init=<not-the-default-init-value>)? It makes no sense for
# ClassVar and InitVar to specify init=<anything>.

@@ -555,17 +612,2 @@

def _find_fields(cls):
# Return a list of Field objects, in order, for this class (and no
# base classes). Fields are found from __annotations__ (which is
# guaranteed to be ordered). Default values are from class
# attributes, if a field has a default. If the default value is
# a Field(), then it contains additional info beyond (and
# possibly including) the actual default value. Pseudo-fields
# ClassVars and InitVars are included, despite the fact that
# they're not real fields. That's dealt with later.
annotations = getattr(cls, '__annotations__', {})
return [_get_field(cls, a_name, a_type)
for a_name, a_type in annotations.items()]
def _set_new_attribute(cls, name, value):

@@ -580,3 +622,51 @@ # Never overwrites an existing attribute. Returns True if the

def _process_class(cls, repr, eq, order, hash, init, frozen):
# Decide if/how we're going to create a hash function. Key is
# (unsafe_hash, eq, frozen, does-hash-exist). Value is the action to
# take. The common case is to do nothing, so instead of providing a
# function that is a no-op, use None to signify that.
def _hash_set_none(cls, fields):
return None
def _hash_add(cls, fields):
flds = [f for f in fields if (f.compare if f.hash is None else f.hash)]
return _hash_fn(flds)
def _hash_exception(cls, fields):
# Raise an exception.
raise TypeError(f'Cannot overwrite attribute __hash__ '
f'in class {cls.__name__}')
#
# +-------------------------------------- unsafe_hash?
# | +------------------------------- eq?
# | | +------------------------ frozen?
# | | | +---------------- has-explicit-hash?
# | | | |
# | | | | +------- action
# | | | | |
# v v v v v
_hash_action = {(False, False, False, False): None,
(False, False, False, True ): None,
(False, False, True, False): None,
(False, False, True, True ): None,
(False, True, False, False): _hash_set_none,
(False, True, False, True ): None,
(False, True, True, False): _hash_add,
(False, True, True, True ): None,
(True, False, False, False): _hash_add,
(True, False, False, True ): _hash_exception,
(True, False, True, False): _hash_add,
(True, False, True, True ): _hash_exception,
(True, True, False, False): _hash_add,
(True, True, False, True ): _hash_exception,
(True, True, True, False): _hash_add,
(True, True, True, True ): _hash_exception,
}
# See https://bugs.python.org/issue32929#msg312829 for an if-statement
# version of this table.
def _process_class(cls, init, repr, eq, order, unsafe_hash, frozen):
# Now that dicts retain insertion order, there's no reason to use

@@ -588,17 +678,41 @@ # an ordered dict. I am leveraging that ordering here, because

setattr(cls, _PARAMS, _DataclassParams(init, repr, eq, order,
unsafe_hash, frozen))
# Find our base classes in reverse MRO order, and exclude
# ourselves. In reversed order so that more derived classes
# override earlier field definitions in base classes.
# As long as we're iterating over them, see if any are frozen.
any_frozen_base = False
has_dataclass_bases = False
for b in cls.__mro__[-1:0:-1]:
# Only process classes that have been processed by our
# decorator. That is, they have a _MARKER attribute.
base_fields = getattr(b, _MARKER, None)
# decorator. That is, they have a _FIELDS attribute.
base_fields = getattr(b, _FIELDS, None)
if base_fields:
has_dataclass_bases = True
for f in base_fields.values():
fields[f.name] = f
if getattr(b, _PARAMS).frozen:
any_frozen_base = True
# Annotations that are defined in this class (not in base
# classes). If __annotations__ isn't present, then this class
# adds no new annotations. We use this to compute fields that
# are added by this class.
# Fields are found from cls_annotations, which is guaranteed to be
# ordered. Default values are from class attributes, if a field
# has a default. If the default value is a Field(), then it
# contains additional info beyond (and possibly including) the
# actual default value. Pseudo-fields ClassVars and InitVars are
# included, despite the fact that they're not real fields.
# That's dealt with later.
cls_annotations = cls.__dict__.get('__annotations__', {})
# Now find fields in our class. While doing so, validate some
# things, and set the default values (as class attributes)
# where we can.
for f in _find_fields(cls):
cls_fields = [_get_field(cls, name, type)
for name, type in cls_annotations.items()]
for f in cls_fields:
fields[f.name] = f

@@ -622,13 +736,32 @@

# Remember all of the fields on our class (including bases). This
# Do we have any Field members that don't also have annotations?
for name, value in cls.__dict__.items():
if isinstance(value, Field) and not name in cls_annotations:
raise TypeError(f'{name!r} is a field but has no type annotation')
# Check rules that apply if we are derived from any dataclasses.
if has_dataclass_bases:
# Raise an exception if any of our bases are frozen, but we're not.
if any_frozen_base and not frozen:
raise TypeError('cannot inherit non-frozen dataclass from a '
'frozen one')
# Raise an exception if we're frozen, but none of our bases are.
if not any_frozen_base and frozen:
raise TypeError('cannot inherit frozen dataclass from a '
'non-frozen one')
# Remember all of the fields on our class (including bases). This also
# marks this class as being a dataclass.
setattr(cls, _MARKER, fields)
setattr(cls, _FIELDS, fields)
# We also need to check if a parent class is frozen: frozen has to
# be inherited down.
is_frozen = frozen or cls.__setattr__ is _frozen_setattr
# Was this class defined with an explicit __hash__? Note that if
# __eq__ is defined in this class, then python will automatically
# set __hash__ to None. This is a heuristic, as it's possible
# that such a __hash__ == None was not auto-generated, but it
# close enough.
class_hash = cls.__dict__.get('__hash__', MISSING)
has_explicit_hash = not (class_hash is MISSING or
(class_hash is None and '__eq__' in cls.__dict__))
# Was this class defined with an __eq__? Used in __hash__ logic.
auto_hash_test= '__eq__' in cls.__dict__ and getattr(cls.__dict__, '__hash__', MISSING) is None
# If we're generating ordering methods, we must be generating

@@ -648,3 +781,3 @@ # the eq methods.

_init_fn(flds,
is_frozen,
frozen,
has_post_init,

@@ -688,44 +821,20 @@ # The name to use for the "self" param

raise TypeError(f'Cannot overwrite attribute {name} '
f'in {cls.__name__}. Consider using '
f'in class {cls.__name__}. Consider using '
'functools.total_ordering')
if is_frozen:
for name, fn in [('__setattr__', _frozen_setattr),
('__delattr__', _frozen_delattr)]:
if _set_new_attribute(cls, name, fn):
raise TypeError(f'Cannot overwrite attribute {name} '
f'in {cls.__name__}')
if frozen:
for fn in _frozen_get_del_attr(cls, field_list):
if _set_new_attribute(cls, fn.__name__, fn):
raise TypeError(f'Cannot overwrite attribute {fn.__name__} '
f'in class {cls.__name__}')
# Decide if/how we're going to create a hash function.
# TODO: Move this table to module scope, so it's not recreated
# all the time.
generate_hash = {(None, False, False): ('', ''),
(None, False, True): ('', ''),
(None, True, False): ('none', ''),
(None, True, True): ('fn', 'fn-x'),
(False, False, False): ('', ''),
(False, False, True): ('', ''),
(False, True, False): ('', ''),
(False, True, True): ('', ''),
(True, False, False): ('fn', 'fn-x'),
(True, False, True): ('fn', 'fn-x'),
(True, True, False): ('fn', 'fn-x'),
(True, True, True): ('fn', 'fn-x'),
}[None if hash is None else bool(hash), # Force bool() if not None.
bool(eq),
bool(frozen)]['__hash__' in cls.__dict__]
# No need to call _set_new_attribute here, since we already know if
# we're overwriting a __hash__ or not.
if generate_hash == '':
# Do nothing.
pass
elif generate_hash == 'none':
cls.__hash__ = None
elif generate_hash in ('fn', 'fn-x'):
if generate_hash == 'fn' or auto_hash_test:
flds = [f for f in field_list
if (f.compare if f.hash is None else f.hash)]
cls.__hash__ = _hash_fn(flds)
else:
assert False, f"can't get here: {generate_hash}"
hash_action = _hash_action[bool(unsafe_hash),
bool(eq),
bool(frozen),
has_explicit_hash]
if hash_action:
# No need to call _set_new_attribute here, since by the time
# we're here the overwriting is unconditional.
cls.__hash__ = hash_action(cls, field_list)

@@ -744,3 +853,3 @@ if not getattr(cls, '__doc__'):

def dataclass(_cls=None, *, init=True, repr=True, eq=True, order=False,
hash=None, frozen=False):
unsafe_hash=False, frozen=False):
"""Returns the same class as was passed in, with dunder methods

@@ -753,9 +862,9 @@ added based on the fields defined in the class.

repr is true, a __repr__() method is added. If order is true, rich
comparison dunder methods are added. If hash is true, a __hash__()
method function is added. If frozen is true, fields may not be
assigned to after instance creation.
comparison dunder methods are added. If unsafe_hash is true, a
__hash__() method function is added. If frozen is true, fields may
not be assigned to after instance creation.
"""
def wrap(cls):
return _process_class(cls, repr, eq, order, hash, init, frozen)
return _process_class(cls, init, repr, eq, order, unsafe_hash, frozen)

@@ -780,3 +889,3 @@ # See if we're being called as @dataclass or @dataclass().

try:
fields = getattr(class_or_instance, _MARKER)
fields = getattr(class_or_instance, _FIELDS)
except AttributeError:

@@ -792,3 +901,3 @@ raise TypeError('must be called with a dataclass type or instance')

"""Returns True if obj is an instance of a dataclass."""
return not isinstance(obj, type) and hasattr(obj, _MARKER)
return not isinstance(obj, type) and hasattr(obj, _FIELDS)

@@ -799,3 +908,3 @@

dataclass."""
return hasattr(obj, _MARKER)
return hasattr(obj, _FIELDS)

@@ -826,2 +935,3 @@

def _asdict_inner(obj, dict_factory):

@@ -840,3 +950,3 @@ if _is_dataclass_instance(obj):

else:
return deepcopy(obj)
return copy.deepcopy(obj)

@@ -867,2 +977,3 @@

def _astuple_inner(obj, tuple_factory):

@@ -881,7 +992,8 @@ if _is_dataclass_instance(obj):

else:
return deepcopy(obj)
return copy.deepcopy(obj)
def make_dataclass(cls_name, fields, *, bases=(), namespace=None, init=True,
repr=True, eq=True, order=False, hash=None, frozen=False):
repr=True, eq=True, order=False, unsafe_hash=False,
frozen=False):
"""Return a new dynamically created dataclass.

@@ -906,3 +1018,3 @@

The parameters init, repr, eq, order, hash, and frozen are passed to
The parameters init, repr, eq, order, unsafe_hash, and frozen are passed to
dataclass().

@@ -932,4 +1044,5 @@ """

return dataclass(cls, init=init, repr=repr, eq=eq, order=order,
hash=hash, frozen=frozen)
unsafe_hash=unsafe_hash, frozen=frozen)
def replace(obj, **changes):

@@ -959,3 +1072,3 @@ """Return a new object replacing specified fields with new values.

for f in getattr(obj, _MARKER).values():
for f in getattr(obj, _FIELDS).values():
if not f.init:

@@ -972,7 +1085,7 @@ # Error if this field is specified in changes.

# Create the new object, which calls __init__() and __post_init__
# (if defined), using all of the init fields we've added and/or
# left in 'changes'.
# If there are values supplied in changes that aren't fields, this
# will correctly raise a TypeError.
# Create the new object, which calls __init__() and
# __post_init__() (if defined), using all of the init fields
# we've added and/or left in 'changes'. If there are values
# supplied in changes that aren't fields, this will correctly
# raise a TypeError.
return obj.__class__(**changes)
Metadata-Version: 1.1
Name: dataclasses
Version: 0.4
Version: 0.5
Summary: An implementation of PEP 557: Data Classes

@@ -5,0 +5,0 @@ Home-page: https://github.com/ericvsmith/dataclasses

@@ -0,5 +1,15 @@

.. image:: https://img.shields.io/pypi/v/dataclasses.svg
This is an implementation of PEP 557, Data Classes. It is a backport
for Python 3.6. Version 0.4 matches Python 3.7 beta 1.
for Python 3.6. Version 0.5 of this repo matches Python 3.7 beta 3.
Because dataclasses will be included in Python 3.7, any discussion of
dataclass features should occur on the python-dev mailing list at
https://mail.python.org/mailman/listinfo/python-dev. At this point
this repo should only be used for historical purposes (it's where the
original dataclasses discussions took place) and for discussion of the
actual backport to Python 3.6.
See https://www.python.org/dev/peps/pep-0557/ for the details.
See https://www.python.org/dev/peps/pep-0557/ for the details of how
Data Classes work.

@@ -10,5 +20,17 @@ A test file can be found at

Example::
Installation
-------------
.. code-block::
pip install dataclasses
Example Usage
-------------
.. code-block::
from dataclasses import dataclass, field
@dataclass

@@ -37,3 +59,4 @@ class InventoryItem:

of this writing, it's also true for all other Python implementations
that claim to be 3.6 compatible, of which there are none. See the
that claim to be 3.6 compatible, of which there are none. Any new
3.6 implementations are expected to have ordered dicts. See the
analysis at the end of this email:

@@ -40,0 +63,0 @@

[egg_info]
tag_build =
tag_date = 0
tag_svn_revision = 0

@@ -5,3 +5,3 @@ from setuptools import setup

name='dataclasses',
version='0.4',
version='0.5',
description='An implementation of PEP 557: Data Classes',

@@ -8,0 +8,0 @@ url='https://github.com/ericvsmith/dataclasses',

Sorry, the diff of this file is too big to display