Skip to content

Commit de61edc

Browse files
committed
Mass backport from py34+
* setuptools_scm * allow py3 (only for compatibility reasons: py3 versions are better) * fix mypy Signed-off-by: Aleksei Stepanov <[email protected]>
1 parent 9b21a02 commit de61edc

File tree

7 files changed

+84
-137
lines changed

7 files changed

+84
-137
lines changed

.travis.yml

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,26 @@ _python:
1111
- &python27
1212
name: "Python 2.7"
1313
python: 2.7
14-
- &python36
15-
name: "Python 3.6"
16-
python: 3.6
1714
- &pypy
1815
name: "PyPy"
1916
python: pypy
17+
- &python34
18+
name: "Python 3.4"
19+
python: "3.4"
20+
- &python35
21+
name: "Python 3.5"
22+
python: "3.5"
23+
- &python36
24+
name: "Python 3.6"
25+
python: "3.6"
26+
- &python37
27+
name: "Python 3.7"
28+
python: "3.7"
29+
dist: xenial
30+
sudo: true
31+
- &pypy3
32+
name: "PyPy3"
33+
python: "pypy3.5"
2034

2135
_helpers:
2236
- &build_package python setup.py bdist_wheel
@@ -41,11 +55,6 @@ after_success:
4155
jobs:
4256
fast_finish: true
4357
include:
44-
- stage: test
45-
<<: *python27
46-
- stage: test
47-
<<: *pypy
48-
4958
- <<: *static_analysis
5059
name: "PyLint"
5160
install:
@@ -85,6 +94,21 @@ jobs:
8594
script:
8695
- pydocstyle advanced_descriptors
8796

97+
- stage: test
98+
<<: *python27
99+
- stage: test
100+
<<: *pypy
101+
- stage: test
102+
<<: *python34
103+
- stage: test
104+
<<: *python35
105+
- stage: test
106+
<<: *python36
107+
- stage: test
108+
<<: *python37
109+
- stage: test
110+
<<: *pypy3
111+
88112
- stage: deploy
89113
# This prevents job from appearing in test plan unless commit is tagged:
90114
if: tag IS present
@@ -109,6 +133,7 @@ jobs:
109133
tags: true
110134
distributions: sdist
111135
skip_upload_docs: true
136+
skip_existing: true
112137

113138
cache: pip
114139
before_cache:

advanced_descriptors/__init__.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,22 @@
1313

1414
"""Advanced descriptors for special cases."""
1515

16+
import pkg_resources
17+
1618
from .separate_class_method import SeparateClassMethod
1719
from .advanced_property import AdvancedProperty
1820

19-
__all__ = ("SeparateClassMethod", "AdvancedProperty")
21+
try:
22+
__version__ = pkg_resources.get_distribution(__name__).version
23+
except pkg_resources.DistributionNotFound:
24+
# package is not installed, try to get from SCM
25+
try:
26+
import setuptools_scm # type: ignore
27+
28+
__version__ = setuptools_scm.get_version()
29+
except ImportError:
30+
pass
2031

21-
__version__ = "1.0.7"
2232
__author__ = "Alexey Stepanov"
2333
__author_email__ = "[email protected]"
2434
__maintainers__ = {

advanced_descriptors/advanced_property.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,10 @@
1515

1616
"""Property with class-wide getter."""
1717

18-
import typing # noqa: F401 # pylint: disable=unused-import
19-
2018
__all__ = ("AdvancedProperty",)
2119

20+
import typing # noqa: F401 # pylint: disable=unused-import
21+
2222

2323
class AdvancedProperty(object):
2424
"""Property with class-wide getter.

advanced_descriptors/separate_class_method.py

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,12 @@
1515

1616
"""Separate class and instance methods with the same name."""
1717

18+
__all__ = ("SeparateClassMethod",)
19+
1820
import typing # noqa: F401 # pylint: disable=unused-import
1921

2022
import six
2123

22-
__all__ = ("SeparateClassMethod",)
23-
2424

2525
class SeparateClassMethod(object):
2626
"""Separate class method and instance methods with the same name.
@@ -93,8 +93,10 @@ class SeparateClassMethod(object):
9393
__slots__ = ("__instance_method", "__class_method")
9494

9595
def __init__(
96-
self, imeth=None, cmeth=None
97-
): # type: (typing.Optional[typing.Callable], typing.Optional[typing.Callable]) -> None
96+
self,
97+
imeth=None, # type: typing.Optional[typing.Callable[..., typing.Any]]
98+
cmeth=None # type: typing.Optional[typing.Callable[..., typing.Any]]
99+
): # type: (...) -> None
98100
"""Separate class method and instance methods.
99101
100102
:param imeth: Instance method
@@ -105,7 +107,11 @@ def __init__(
105107
self.__instance_method = imeth
106108
self.__class_method = cmeth
107109

108-
def __get__(self, instance, owner): # type: (typing.Optional[typing.Any], typing.Any) -> typing.Callable
110+
def __get__(
111+
self,
112+
instance, # type: typing.Optional[typing.Any]
113+
owner # type: typing.Any
114+
): # type: (...) -> typing.Callable[..., typing.Any]
109115
"""Get descriptor.
110116
111117
:return: class method or instance method depends on call behavior
@@ -130,7 +136,10 @@ def instance_method(*args, **kwargs): # type: (typing.Any, typing.Any) -> typin
130136

131137
return instance_method
132138

133-
def instance_method(self, imeth): # type: (typing.Optional[typing.Callable]) -> SeparateClassMethod
139+
def instance_method(
140+
self,
141+
imeth # type: typing.Optional[typing.Callable[..., typing.Any]]
142+
): # type: (...) -> SeparateClassMethod
134143
"""Descriptor to change instance method.
135144
136145
:param imeth: New instance method.
@@ -141,7 +150,7 @@ def instance_method(self, imeth): # type: (typing.Optional[typing.Callable]) ->
141150
self.__instance_method = imeth
142151
return self
143152

144-
def class_method(self, cmeth): # type: (typing.Optional[typing.Callable]) -> SeparateClassMethod
153+
def class_method(self, cmeth): # type: (typing.Optional[typing.Callable[..., typing.Any]]) -> SeparateClassMethod
145154
"""Descriptor to change class method.
146155
147156
:param cmeth: New class method.
@@ -153,15 +162,15 @@ def class_method(self, cmeth): # type: (typing.Optional[typing.Callable]) -> Se
153162
return self
154163

155164
@property
156-
def imeth(self): # type: () -> typing.Optional[typing.Callable]
165+
def imeth(self): # type: () -> typing.Optional[typing.Callable[..., typing.Any]]
157166
"""Instance method instance.
158167
159168
:rtype: typing.Optional[typing.Callable]
160169
"""
161170
return self.__instance_method
162171

163172
@property
164-
def cmeth(self): # type: () -> typing.Optional[typing.Callable]
173+
def cmeth(self): # type: () -> typing.Optional[typing.Callable[..., typing.Any]]
165174
"""Class method instance.
166175
167176
:rtype: typing.Optional[typing.Callable]

doc/source/conf.py

Lines changed: 5 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -13,110 +13,10 @@
1313
# All configuration values have a default; values that are commented out
1414
# serve to show the default.
1515

16-
import ast
17-
import collections
18-
import os.path
19-
import sys
20-
21-
PY3 = sys.version_info[:2] > (2, 7)
22-
PY34 = sys.version_info[:2] > (3, 3)
23-
24-
with open(
25-
os.path.join(
26-
os.path.dirname(__file__), '..', '..',
27-
'advanced_descriptors', '__init__.py'
28-
)
29-
) as f:
30-
source = f.read()
31-
32-
33-
# noinspection PyUnresolvedReferences
34-
def get_simple_vars_from_src(src):
35-
"""Get simple (string/number/boolean and None) assigned values from source.
36-
37-
:param src: Source code
38-
:type src: str
39-
:returns: OrderedDict with keys, values = variable names, values
40-
:rtype: typing.Dict[
41-
str,
42-
typing.Union[
43-
str, bytes,
44-
int, float, complex,
45-
list, set, dict, tuple,
46-
None,
47-
]
48-
]
49-
50-
Limitations: Only defined from scratch variables.
51-
Not supported by design:
52-
* Imports
53-
* Executable code, including string formatting and comprehensions.
54-
55-
Examples:
56-
57-
>>> string_sample = "a = '1'"
58-
>>> get_simple_vars_from_src(string_sample)
59-
OrderedDict([('a', '1')])
60-
61-
>>> int_sample = "b = 1"
62-
>>> get_simple_vars_from_src(int_sample)
63-
OrderedDict([('b', 1)])
64-
65-
>>> list_sample = "c = [u'1', b'1', 1, 1.0, 1j, None]"
66-
>>> result = get_simple_vars_from_src(list_sample)
67-
>>> result == collections.OrderedDict(
68-
... [('c', [u'1', b'1', 1, 1.0, 1j, None])]
69-
... )
70-
True
71-
72-
>>> iterable_sample = "d = ([1], {1: 1}, {1})"
73-
>>> get_simple_vars_from_src(iterable_sample)
74-
OrderedDict([('d', ([1], {1: 1}, {1}))])
75-
76-
>>> multiple_assign = "e = f = g = 1"
77-
>>> get_simple_vars_from_src(multiple_assign)
78-
OrderedDict([('e', 1), ('f', 1), ('g', 1)])
79-
"""
80-
ast_data = (
81-
ast.Str, ast.Num,
82-
ast.List, ast.Set, ast.Dict, ast.Tuple
83-
)
84-
if PY3:
85-
ast_data += (ast.Bytes,)
86-
if PY34:
87-
ast_data += (ast.NameConstant,)
88-
89-
tree = ast.parse(src)
90-
91-
result = collections.OrderedDict()
92-
93-
for node in ast.iter_child_nodes(tree):
94-
if not isinstance(node, ast.Assign): # We parse assigns only
95-
continue
96-
try:
97-
if isinstance(node.value, ast_data):
98-
value = ast.literal_eval(node.value)
99-
elif isinstance( # NameConstant in python < 3.4
100-
node.value, ast.Name
101-
) and isinstance(
102-
node.value.ctx, ast.Load # Read constant
103-
):
104-
value = ast.literal_eval(node.value)
105-
else:
106-
continue
107-
except ValueError:
108-
continue
109-
for tgt in node.targets:
110-
if isinstance(
111-
tgt, ast.Name
112-
) and isinstance(
113-
tgt.ctx, ast.Store
114-
):
115-
result[tgt.id] = value
116-
return result
117-
118-
119-
variables = get_simple_vars_from_src(source)
16+
import pkg_resources
17+
18+
release = pkg_resources.get_distribution("Advanced-Descriptors").version
19+
version = '.'.join(release.split('.')[:2])
12020

12121
# If extensions (or modules to document with autodoc) are in another directory,
12222
# add these directories to sys.path here. If the directory is relative to the
@@ -168,9 +68,8 @@ def get_simple_vars_from_src(src):
16868
# built documents.
16969
#
17070
# The short X.Y version.
171-
version = variables['__version__']
71+
17272
# The full version, including alpha/beta/rc tags.
173-
release = variables['__version__']
17473

17574
# The language for content autogenerated by Sphinx. Refer to documentation
17675
# for a list of supported languages.

setup.py

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,11 @@ def get_simple_vars_from_src(src):
119119
"License :: OSI Approved :: Apache Software License",
120120
"Programming Language :: Python :: 2",
121121
"Programming Language :: Python :: 2.7",
122+
"Programming Language :: Python :: 3",
123+
"Programming Language :: Python :: 3.4",
124+
"Programming Language :: Python :: 3.5",
125+
"Programming Language :: Python :: 3.6",
126+
"Programming Language :: Python :: 3.7",
122127
"Programming Language :: Python :: Implementation :: CPython",
123128
"Programming Language :: Python :: Implementation :: PyPy",
124129
]
@@ -133,22 +138,25 @@ def get_simple_vars_from_src(src):
133138
"{name} <{email}>".format(name=name, email=email) for name, email in variables["__maintainers__"].items()
134139
),
135140
url=variables["__url__"],
136-
version=variables["__version__"],
137141
license=variables["__license__"],
138142
description=variables["__description__"],
139143
long_description=long_description,
140144
classifiers=classifiers,
141145
keywords=keywords,
142-
python_requires=">=2.7.5,<3.0",
146+
python_requires=">=2.7.5,!=3.0,!=3.1,!=3.2,!=3.3",
143147
# While setuptools cannot deal with pre-installed incompatible versions,
144148
# setting a lower bound is not harmful - it makes error messages cleaner. DO
145149
# NOT set an upper bound on setuptools, as that will lead to uninstallable
146150
# situations as progressive releases of projects are done.
147151
# Blacklist setuptools 34.0.0-34.3.2 due to https://github.com/pypa/setuptools/issues/951
148152
# Blacklist setuptools 36.2.0 due to https://github.com/pypa/setuptools/issues/1086
149-
setup_requires="setuptools >= 21.0.0,!=24.0.0,"
150-
"!=34.0.0,!=34.0.1,!=34.0.2,!=34.0.3,!=34.1.0,!=34.1.1,!=34.2.0,!=34.3.0,!=34.3.1,!=34.3.2,"
151-
"!=36.2.0",
153+
setup_requires=[
154+
"setuptools >= 21.0.0,!=24.0.0,"
155+
"!=34.0.0,!=34.0.1,!=34.0.2,!=34.0.3,!=34.1.0,!=34.1.1,!=34.2.0,!=34.3.0,!=34.3.1,!=34.3.2,"
156+
"!=36.2.0",
157+
"setuptools_scm",
158+
],
159+
use_scm_version=True,
152160
install_requires=required,
153161
package_data={"advanced_descriptors": ["py.typed"]},
154162
)

tox.ini

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
[tox]
77
minversion = 2.0
8-
envlist = black, pep8, pylint, mypy, bandit, pep257, py{27,py}, docs,
8+
envlist = black, pep8, pylint, mypy, bandit, pep257, py{27,py,34,35,36,37,py3}, docs,
99
skipsdist = True
1010
skip_missing_interpreters = True
1111

@@ -30,10 +30,6 @@ commands =
3030
[testenv:venv]
3131
commands = {posargs:}
3232

33-
[tox:travis]
34-
2.7 = install, py27
35-
pypy = install, pypy,
36-
3733
[testenv:pep8]
3834
deps =
3935
flake8

0 commit comments

Comments
 (0)