Skip to content

Commit db97952

Browse files
committed
Python 2.7 is now supported. Fixed #5
- assume that all string constants defined inside the code are unicode strings - transformedd all type hints into type hint comments, made typing import optional - removed one forbidden construct - using StringIO from StringIO (with added context manager behaviour) - using six.with_metaclass - python 2.7 declared in travis and setup.py Also made travis script more robust for forks/PR
1 parent 3b13ac0 commit db97952

9 files changed

Lines changed: 287 additions & 96 deletions

File tree

.travis.yml

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ cache: pip
44

55
python:
66
# - "2.6"
7-
# - "2.7"
7+
- "2.7"
88
# - "3.2"
99
# - "3.3"
1010
# - "3.4"
@@ -62,17 +62,22 @@ after_success:
6262
# mkdocs gh-deploy requires special care :
6363
# ---grant the possibility to push on the repo---
6464
- openssl aes-256-cbc -K $encrypted_a3be41b6f539_key -iv $encrypted_a3be41b6f539_iv -in ci_tools/github_travis_rsa.enc -out ci_tools/github_travis_rsa -d
65-
- chmod 600 ci_tools/github_travis_rsa
66-
- eval `ssh-agent -s` # launch the authentication agent
67-
- ssh-add ci_tools/github_travis_rsa # register the key
68-
- git config user.name "Automatic Publish"
69-
- git config user.email "sylvain.marie@schneider-electric.com"
70-
- git remote add gh-remote "${GH_REF}";
71-
- git fetch gh-remote && git fetch gh-remote gh-pages:gh-pages;
72-
# push but only if this is not a build triggered by a pull request
73-
# note: here we use the --dirty flag so that mkdocs does not clean the additional reports that we copied in the site
74-
- if [ "${TRAVIS_PULL_REQUEST}" = "false" ] && [ "${TRAVIS_PYTHON_VERSION}" = "3.5" ]; then echo "Pushing to github"; PYTHONPATH=yamlable/ mkdocs gh-deploy -v --dirty -f docs/mkdocs.yml --remote-name gh-remote; git push gh-remote gh-pages; fi;
75-
# - if [ "${TRAVIS_PULL_REQUEST}" = "false" ] && [ "${TRAVIS_PYTHON_VERSION}" = "3.5" ]; then echo "Pushing to github"; git push gh-remote gh-pages; fi;
65+
# If the output file does not exist, that is because the secret is invalid. This can happen in forked repos so do not fail the build
66+
- |
67+
if [ -s "ci_tools/github_travis_rsa" ]; then
68+
chmod 600 ci_tools/github_travis_rsa
69+
eval `ssh-agent -s` # launch the authentication agent
70+
ssh-add ci_tools/github_travis_rsa # register the key
71+
git config user.name "Automatic Publish"
72+
git config user.email "sylvain.marie@schneider-electric.com"
73+
git remote add gh-remote "${GH_REF}";
74+
git fetch gh-remote && git fetch gh-remote gh-pages:gh-pages;
75+
# push but only if this is not a build triggered by a pull request
76+
# note: here we use the --dirty flag so that mkdocs does not clean the additional reports that we copied in the site
77+
if [ "${TRAVIS_PULL_REQUEST}" = "false" ] && [ "${TRAVIS_PYTHON_VERSION}" = "3.5" ]; then echo "Pushing to github"; PYTHONPATH=yamlable/ mkdocs gh-deploy -v --dirty -f docs/mkdocs.yml --remote-name gh-remote; git push gh-remote gh-pages; fi;
78+
else
79+
echo "File 'ci_tools/github_travis_rsa' has not been created, please check your encrypted repo token in .travis.yml, on the line starting with 'openssl aes-256-cbc...'"
80+
fi
7681
7782
deploy:
7883
provider: pypi

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@
9494
# that you indicate whether you support Python 2, Python 3 or both.
9595
# 'Programming Language :: Python :: 2',
9696
# 'Programming Language :: Python :: 2.6',
97-
# 'Programming Language :: Python :: 2.7',
97+
'Programming Language :: Python :: 2.7',
9898
# 'Programming Language :: Python :: 3',
9999
# 'Programming Language :: Python :: 3.3',
100100
# 'Programming Language :: Python :: 3.4',

yamlable/__init__.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,16 @@
1-
from yamlable.base import AbstractYamlObject, NONE_IGNORE_CHECKS, Y, read_yaml_node_as_dict
2-
from yamlable.main import YamlCodec, register_yamlable_codec, yaml_info_decorate, yaml_info, YamlAble, YA, \
1+
try: # python 3.5+
2+
from yamlable.base import Y
3+
except ImportError:
4+
pass
5+
from yamlable.base import AbstractYamlObject, NONE_IGNORE_CHECKS, read_yaml_node_as_dict
6+
7+
try: # python 3.5+
8+
from yamlable.main import YA
9+
except ImportError:
10+
pass
11+
from yamlable.main import YamlCodec, register_yamlable_codec, yaml_info_decorate, yaml_info, YamlAble, \
312
AbstractYamlAble, YAMLABLE_PREFIX
13+
414
from yamlable.yaml_objects import YamlObject2, ABCYAMLMeta, YAMLObjectMetaclassStrict
515

616
__all__ = ['base', 'main', 'yaml_objects',

yamlable/base.py

Lines changed: 61 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,40 @@
1-
from abc import ABC
1+
from abc import ABCMeta
22
from collections import OrderedDict
3-
from io import TextIOBase, StringIO
4-
from typing import Union, TypeVar, Dict, Any
5-
from warnings import warn
63

74
try:
8-
from typing import Type
5+
# Python 2 only:
6+
from StringIO import StringIO
7+
8+
# create a variant that can serve as a context manager
9+
class StringIO(StringIO):
10+
def __enter__(self):
11+
return self
12+
def __exit__(self, exception_type, exception_value, traceback):
13+
self.close()
14+
915
except ImportError:
10-
pass # normal for old versions of typing
16+
# (IOBase is only used in type hints)
17+
from io import IOBase, StringIO
18+
19+
from warnings import warn
1120

21+
import six
1222

13-
Y = TypeVar('Y', bound='AbstractYamlObject')
23+
try: # python 3.5+
24+
from typing import Union, TypeVar, Dict, Any
25+
26+
Y = TypeVar('Y', bound='AbstractYamlObject')
27+
28+
except ImportError:
29+
pass
30+
31+
try: # python 3.5.4+
32+
from typing import Type
33+
except ImportError:
34+
pass # normal for old versions of typing
1435

1536

16-
class AbstractYamlObject(ABC):
37+
class AbstractYamlObject(six.with_metaclass(ABCMeta, object)):
1738
"""
1839
Adds convenient methods load(s)_yaml/dump(s)_yaml to any object, to call pyyaml features directly on the object or
1940
on the object class.
@@ -22,24 +43,30 @@ class AbstractYamlObject(ABC):
2243
Default implementation uses vars(self) and cls(**dct), but subclasses can override.
2344
"""
2445

25-
def __to_yaml_dict__(self) -> Dict[str, Any]:
46+
def __to_yaml_dict__(self):
47+
# type: (...) -> Dict[str, Any]
2648
"""
2749
Implementors should transform the object into a dictionary containing all information necessary to decode the
2850
object in the future. That dictionary will be serialized as a YAML mapping.
2951
3052
Default implementation returns vars(self). TODO maybe some day we'll need to rather make a copy...?
3153
:return:
3254
"""
33-
# Legacy compliance TODO remove in future version
55+
# Legacy compliance with old 'not dunder' method name TODO remove in future version
3456
if 'to_yaml_dict' in dir(self):
3557
warn(type(self).__name__ + " still uses the legacy method name 'to_yaml_dict'. This name will not be "
3658
"supported in future version, please use '__to_yaml_dict__' instead")
3759
return self.to_yaml_dict()
3860

61+
# Default: return vars
3962
return vars(self)
4063

4164
@classmethod
42-
def __from_yaml_dict__(cls: 'Type[Y]', dct: Dict[Any, Any], yaml_tag: str) -> Y:
65+
def __from_yaml_dict__(cls, # type: Type[Y]
66+
dct, # type: Dict[str, Any]
67+
yaml_tag # type: str
68+
):
69+
# type: (...) -> Y
4370
"""
4471
Implementors should transform the given dictionary (read from yaml by the pyYaml stack) into an object instance.
4572
The yaml tag associated to this object, read in the yaml document, is provided in parameter.
@@ -54,15 +81,21 @@ def __from_yaml_dict__(cls: 'Type[Y]', dct: Dict[Any, Any], yaml_tag: str) -> Y:
5481
against is_json_schema_id_supported)
5582
:return:
5683
"""
57-
# Legacy compliance TODO remove in future version
84+
# Legacy compliance with old 'not dunder' method name TODO remove in future version
5885
if 'from_yaml_dict' in dir(cls):
5986
warn(cls.__name__ + " still uses the legacy method name 'from_yaml_dict'. This name will not be "
6087
"supported in future version, please use '__from_yaml_dict__' instead")
6188
return cls.from_yaml_dict(dct, yaml_tag)
6289

90+
# Default: call constructor with all keyword arguments
6391
return cls(**dct)
6492

65-
def dump_yaml(self, file_path_or_stream: Union[str, TextIOBase], safe: bool = True, **pyyaml_kwargs):
93+
def dump_yaml(self,
94+
file_path_or_stream, # type: Union[str, IOBase, StrinIO]
95+
safe=True, # type: bool
96+
**pyyaml_kwargs # type: Dict[str, Any]
97+
):
98+
# type: (...) -> None
6699
"""
67100
Dumps this object to a yaml file or stream using pyYaml.
68101
@@ -85,7 +118,11 @@ def dump_yaml(self, file_path_or_stream: Union[str, TextIOBase], safe: bool = Tr
85118
else:
86119
dump(self, f, **pyyaml_kwargs)
87120

88-
def dumps_yaml(self, safe: bool = True, **pyyaml_kwargs):
121+
def dumps_yaml(self,
122+
safe=True, # type: bool
123+
**pyyaml_kwargs # type: Dict[str, Any]
124+
):
125+
# type: (...) -> str
89126
"""
90127
Dumps this object to a yaml string and returns it.
91128
@@ -100,7 +137,11 @@ def dumps_yaml(self, safe: bool = True, **pyyaml_kwargs):
100137
return dump(self, **pyyaml_kwargs)
101138

102139
@classmethod
103-
def loads_yaml(cls: 'Type[Y]', yaml_str: str, safe: bool=True) -> Y:
140+
def loads_yaml(cls, # type: Type[Y]
141+
yaml_str, # type: str
142+
safe=True # type: bool
143+
):
144+
# type: (...) -> Y
104145
"""
105146
Utility method to load an instance of this class from the provided yaml string. This methods only returns
106147
successfully if the result is an instance of `cls`.
@@ -112,7 +153,11 @@ def loads_yaml(cls: 'Type[Y]', yaml_str: str, safe: bool=True) -> Y:
112153
return cls.load_yaml(StringIO(yaml_str), safe=safe)
113154

114155
@classmethod
115-
def load_yaml(cls: 'Type[Y]', file_path_or_stream: Union[str, TextIOBase], safe: bool=True) -> Y:
156+
def load_yaml(cls, # type: Type[Y]
157+
file_path_or_stream, # type: Union[str, IOBase, StringIO]
158+
safe=True # type: bool
159+
):
160+
# type: (...) -> Y
116161
"""
117162
Parses the given file path or stream as a yaml document. This methods only returns successfully if the result
118163
is an instance of `cls`.

0 commit comments

Comments
 (0)