Skip to content

Commit

Permalink
Version 1.1.0: fixes, conda build recipie & more
Browse files Browse the repository at this point in the history
  • Loading branch information
jacob414 committed Oct 19, 2020
1 parent ca9b69e commit ac4f9a4
Show file tree
Hide file tree
Showing 12 changed files with 199 additions and 65 deletions.
20 changes: 20 additions & 0 deletions .codeclimate.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
version: "2"
engines:
sonar-python:
enabled: true
duplication:
enabled: true
config:
languages:
ruby:
javascript:
- enabled: false
python:
python_version: 3
php:
checks:
argument-count:
enabled: false
exclude_patterns:
- "./htmlcov/"
- "contextdecorator.py"
27 changes: 8 additions & 19 deletions altered/__init__.py
Original file line number Diff line number Diff line change
@@ -1,42 +1,33 @@
from __future__ import absolute_import
from functools import wraps

from altered.base import (dictget, dictset, dictdel, change, restore, Expando,
E, forget, dictlike)
from altered.base import (change, restore, Expando, E, forget, anyget, anyset,
anydel)

from altered.base import (Expando, E, forget, anyget, anyset, anydel) # NOQA

try:
from contextlib import ContextDecorator
except ImportError:
from .contextdecorator import ContextDecorator


def changers(obj):
"""
Chooses suitable change operations for `obj`.
"""
return (dictlike(obj) and (dictget, dictset, dictdel)
or (getattr, setattr, delattr))


class state(ContextDecorator):
"""
This combined context manager and decorator is the main API to use
Altered States.
"""

def __init__(self, orig, **attrs):
self.orig = orig
self.attrs = attrs
return super(state, self).__init__()

def __enter__(self):
self.getter, self.setter, self.deleter = changers(self.orig)
self.diff = change(self.orig, self.getter, self.setter, self.deleter,
**self.attrs)
self.diff = change(self.orig, **self.attrs)
return self

def __exit__(self, *args, **kw):
restore(self.orig, self.diff, self.getter, self.setter, self.deleter)
restore(self.orig, self.diff)
return False

def __call__(self, f):
Expand All @@ -54,11 +45,9 @@ def alter(obj, **changes):
`state`, but returns a function that will restore the changes at a
later point.
"""
getter, setter, deleter = changers(obj)

diff = change(obj, getter, setter, deleter, **changes)
diff = change(obj, **changes)

def restoration():
restore(obj, diff, getter, setter, deleter)
restore(obj, diff)

return restoration
104 changes: 72 additions & 32 deletions altered/base.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import sys
import os

try:
from future import standard_library
Expand All @@ -8,10 +8,8 @@
pass


if int(sys.version_info[0]) < 3:
Mapping = collections.Mapping
else:
Mapping = collections.abc.Mapping
def isenv(x):
return x is os.environ


class Expando(object):
Expand Down Expand Up @@ -39,6 +37,18 @@ def __repr__(self):
return '<%s object at 0x%x%s%s>' % (type(self).__name__, id(self),
': ' if a else '', a)

def __getitem__(self, key):
"Get attribute of `Expando` via index."
return self.__dict__[key]

def __setitem__(self, key, val):
"Set attribute of `Expando` via index."
self.__dict__[key] = val

def __delattr__(self, key):
"Delete attribute of `Expando` via index."
del self.__dict__[key]

def __bool__(self):
return bool(self.__dict__)

Expand All @@ -53,7 +63,7 @@ class forget(object):
"""


def change(orig, getter, setter, deleter, **attrs):
def change(orig, **attrs):
"""
Alter `orig` using the functions `getter`, `setter` and `deleter`
with the contents of `**attrs`. The get/set/delete use the same
Expand All @@ -65,42 +75,72 @@ def change(orig, getter, setter, deleter, **attrs):
"""
diff = {}
for key, val in attrs.items():
diff[key] = getter(orig, key, forget)
diff[key] = anyget(orig, key)
if val is forget:
deleter(orig, key)
anydel(orig, key)
else:
setter(orig, key, val)
anyset(orig, key, val)
return diff


def restore(orig, diff, getter, setter, deleter):
def restore(orig, diff):
"""
Takes a diff produced by `change()` and applies it to `orig` to
make it revert to the state it had before `change()` was called on
it.
"""
for key, old in diff.items():
if old is forget:
deleter(orig, key)
anydel(orig, key)
else:
setter(orig, key, old)


def dictlike(cand):
"Determines if `dict` or `object` semantics should be used"
return isinstance(cand, Mapping)


def dictget(dct, key, default):
"Provides `getattr` semantics for modifying dictionaries."
return dct.get(key, default)


def dictset(dct, key, val):
"Provides `setattr` semantics for modifying dictionaries."
dct[key] = val


def dictdel(dct, key):
"Provides `delattr` semantics for modifiyng dictionaries."
del dct[key]
anyset(orig, key, old)


class NoDefault:
"Marker for not using a default value"


def anyget(x, key, default=NoDefault):
if isenv(x):
# os.environ have always been and still is a nasty exception
# to all rules...
return key in x and x[key] or forget
try:
return getattr(x, key)
except AttributeError:
try:
return x[key]
except KeyError:
pass

if default is NoDefault:
return forget
else:
return default


def anyset(x, key, value):
if isenv(x):
# os.environ have always been and still is a nasty exception
# to all rules... here, a setattr would not raise but wouldn't
# update os.environ
x[key] = value
return
try:
setattr(x, key, value)
except AttributeError:
x[key] = value


def anydel(x, key):
if isenv(x):
# os.environ have always been and still is a nasty exception
# to all rules...
del os.environ[key]
os.unsetenv(key)
return

try:
delattr(x, key)
except AttributeError:
del x[key]
2 changes: 1 addition & 1 deletion altered/meta.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name = 'altered_states'
version = '1.0.9'
version = '1.1.0'
licence = 'Apache Licence 2.0'
author = 'Jacob Oscarson'
author_email = 'jacob@414soft.com'
Expand Down
2 changes: 2 additions & 0 deletions conda.recipe/bld.bat
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
"%PYTHON%" setup.py install
if errorlevel 1 exit 1
1 change: 1 addition & 0 deletions conda.recipe/build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
$PYTHON setup.py install
38 changes: 38 additions & 0 deletions conda.recipe/meta.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{% set name = "altered_states" %}
{% set version = "1.1.0" %}

package:
name: "{{ name|lower }}"
version: "{{ version }}"

source:
git_rev:
git_url: https://github.com/Plexical/altered.states.git

requirements:
build:
- python
- setuptools

run:
- python

test:
requires:
- pytest
- pytest-cov

imports:
- altered

about:
home: "https://github.com/Plexical/altered.states"
license: Apache v2
licencs_file: LICENCE.rst
summary: "Reversible state changes"
doc_url: "https://https://altered-states.readthedocs.io/"
dev_url:

extra:
recipe-maintainers:
- jacob414
4 changes: 4 additions & 0 deletions pytest.ini
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# content of pytest.ini
[pytest]
addopts = --doctest-glob=*.rst
markers =
wbox: marks tests as white-box test (knowns about implementation details)
bbox: marks tests as black-box test (no knownledge about implementation details)

2 changes: 2 additions & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[flake8]
exclude = test_* docs/conf.py ./altered/contextdecorator.py

0 comments on commit ac4f9a4

Please sign in to comment.