Skip to content

Commit

Permalink
Merge pull request #51 from fizyk/squash_compose
Browse files Browse the repository at this point in the history
Squash most of the compose sections to the defaults
  • Loading branch information
fizyk authored Dec 15, 2020
2 parents 722abc3 + b0698d9 commit 61b6c39
Show file tree
Hide file tree
Showing 10 changed files with 68 additions and 80 deletions.
4 changes: 4 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# CHANGES

## Unreleased

* [code] Use default arguments instead of compose sections.

## 0.4.0

Expand Down Expand Up @@ -80,6 +83,7 @@ tablename to split the word, e.g.:
>>> class OperatingScale(Base, BaseMixin):
... __tablename__ 'operating_scales'
...

>>> OperatingScale.class_name
'Operating Scale'

Expand Down
4 changes: 2 additions & 2 deletions src/pyramid_basemodel/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ class and ``bind_engine`` function.
classImplements(Base, IDeclarativeBase)


class classproperty(object):
class classproperty:
"""A basic [class property](http://stackoverflow.com/a/3203659)."""

def __init__(self, getter):
Expand All @@ -62,7 +62,7 @@ def __get__(self, instance, owner):
return self.getter(owner)


class BaseMixin(object):
class BaseMixin:
"""
Default Base Model Mixin.
Expand Down
16 changes: 2 additions & 14 deletions src/pyramid_basemodel/blob.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,21 +93,13 @@ def update(self, name, file_like_object=None):
if file_like_object is not None:
self.value = file_like_object.read()

def update_from_url(self, url, should_unzip=False, requests=None, gzip_cls=None, io_cls=None):
def update_from_url(self, url, should_unzip=False, requests=requests_lib, gzip_cls=GzipFile, io_cls=StringIO):
"""
Update value from url's content.
Update ``self.value`` to be the contents of the file downloaded
from the ``url`` provided.
"""
# Compose.
if requests is None:
requests = requests_lib
if gzip_cls is None:
gzip_cls = GzipFile
if io_cls is None:
io_cls = StringIO

# Download the file, raising an exception if the download fails
# after retrying once.
attempts = 0
Expand All @@ -127,12 +119,8 @@ def update_from_url(self, url, should_unzip=False, requests=None, gzip_cls=None,
else: # Read the response into ``self.value``.
self.value = r.content

def get_as_named_tempfile(self, should_close=False, named_tempfile_cls=None):
def get_as_named_tempfile(self, should_close=False, named_tempfile_cls=NamedTemporaryFile):
"""Read ``self.value`` into and return a named temporary file."""
# Compose.
if named_tempfile_cls is None:
named_tempfile_cls = NamedTemporaryFile

# Prepare the temp file.
f = NamedTemporaryFile(delete=False)

Expand Down
22 changes: 4 additions & 18 deletions src/pyramid_basemodel/container.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,16 +34,12 @@
logger = logging.getLogger(__name__)


def slug_validator(node, value, regexp=None):
def slug_validator(node, value, regexp=valid_slug):
"""
Validate slug.
Defaults to using a slug regexp.
"""
# Compose.
if regexp is None:
regexp = valid_slug

# Raise a ValueError.
if not regexp.match(value):
raise ValueError(f"{value} is not a valid slug.")
Expand Down Expand Up @@ -128,7 +124,7 @@ def __init__(self, request, model_cls, key=None, parent=None, **kwargs):
self.validator = self._validator


class InstanceTraversalMixin(object):
class InstanceTraversalMixin:
"""Provide a default __parent__ implementation for traversal."""

request = None
Expand All @@ -154,12 +150,8 @@ def get_container(self):
return parent
target = parent

def locatable(self, context, key, provides=None):
def locatable(self, context, key, provides=alsoProvides):
"""Make a context object locatable and pass on the request."""
# Compose.
if provides is None:
provides = alsoProvides

if not hasattr(context, "__name__"):
context.__name__ = key
context._located_parent = self
Expand All @@ -169,14 +161,8 @@ def locatable(self, context, key, provides=None):
return context

@property
def __parent__(self, container_cls=None, session=None):
def __parent__(self, container_cls=BaseModelContainer, session=Session):
"""Either return ``self.parent``, or a model container object."""
# Compose.
if container_cls is None:
container_cls = BaseModelContainer
if session is None:
session = Session

# If the context has been located, return the container.
if hasattr(self, "_located_parent"):
return self._located_parent
Expand Down
14 changes: 4 additions & 10 deletions src/pyramid_basemodel/mixin.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
logger = logging.getLogger(__name__)


class PolymorphicBaseMixin(object):
class PolymorphicBaseMixin:
"""
PolymorphicMixin streamline inheritance.
Expand All @@ -39,7 +39,7 @@ def __mapper_args__(self):
return {"polymorphic_on": self.discriminator, "polymorphic_identity": self.__class__.__name__.lower()}


class PolymorphicMixin(object):
class PolymorphicMixin:
"""
PolymorphicMixin streamline inheritance.
Expand All @@ -55,7 +55,7 @@ def __mapper_args__(self):
return {"polymorphic_identity": self.__class__.__name__.lower()}


class TouchMixin(object):
class TouchMixin:
"""Provides ``touch`` and ``propagate_touch`` methods."""

def propagate_touch(self):
Expand All @@ -67,14 +67,8 @@ def propagate_touch(self):
update relations in an attribute event handler.
"""

def touch(self, propagate=True, now=None, save=None):
def touch(self, propagate=True, now=datetime.utcnow, save=save_to_db):
"""Update self.modified."""
# Compose.
if now is None:
now = datetime.utcnow
if save is None:
save = save_to_db

# Update self's modified date.
self.modified = now()
save(self)
Expand Down
8 changes: 2 additions & 6 deletions src/pyramid_basemodel/root.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,18 +23,14 @@


@implementer(ILocation)
class BaseRoot(object):
class BaseRoot:
"""Base class for traversal factories."""

__name__ = ""
__parent__ = None

def locatable(self, context, key, provides=None):
def locatable(self, context, key, provides=alsoProvides):
"""Make a context object locatable and return it."""
# Compose.
if provides is None:
provides = alsoProvides

if not hasattr(context, "__name__"):
context.__name__ = key
context._located_parent = self
Expand Down
19 changes: 10 additions & 9 deletions src/pyramid_basemodel/slug.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
logger = logging.getLogger(__name__)


class BaseSlugNameMixin(object):
class BaseSlugNameMixin:
"""
Base mixin delivering a slug functionality.
Expand All @@ -51,7 +51,15 @@ def name(cls):
"""Get human readable name, e.g.: `Foo Bar`."""
return Column(Unicode(cls._max_slug_length), nullable=False)

def set_slug(self, candidate=None, **kwargs):
def set_slug(
self,
candidate=None,
gen_digest=generate_random_digest,
inspect=sa_inspect,
session=Session,
to_slug=slugify.slugify,
unique=ensure_unique,
):
"""
Generate and set a unique ``self.slug`` from ``self.name``.
Expand All @@ -61,13 +69,6 @@ def set_slug(self, candidate=None, **kwargs):
:param to_slug: slugify function
:param unique: unique function
"""
# Compose.
gen_digest = kwargs.get("gen_digest", generate_random_digest)
inspect = kwargs.get("inspect", sa_inspect)
session = kwargs.get("session", Session)
to_slug = kwargs.get("to_slug", slugify.slugify)
unique = kwargs.get("unique", ensure_unique)

# Generate a candidate slug.
if candidate is None:
if self.name:
Expand Down
12 changes: 3 additions & 9 deletions src/pyramid_basemodel/tree.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,10 @@ class BaseContentRoot(BaseRoot):
apex = None # e.g.: (Design, IDesignsContainer, {})
mapping = {} # {u'formats': (FileFormat, IFileFormatsContainer, {}), ...}

def container_factory(self, item, key, provides=None, default_cls=None, interface_cls=None):
def container_factory(
self, item, key, provides=alsoProvides, default_cls=BaseModelContainer, interface_cls=Interface
):
"""Return an instantiated and interface providing container."""
# Compose.
if provides is None:
provides = alsoProvides
if default_cls is None:
default_cls = BaseModelContainer
if interface_cls is None:
interface_cls = Interface

# Unpack the mapping item.
model_cls, container_cls_or_interface, kwargs = item

Expand Down
14 changes: 2 additions & 12 deletions src/pyramid_basemodel/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,38 +11,28 @@
logger = logging.getLogger(__name__)


def generate_random_digest(num_bytes=28, urandom=None, to_hex=None):
def generate_random_digest(num_bytes=28, urandom=os.urandom, to_hex=hexlify):
"""
Generate a random hash and returns the hex digest as a unicode string.
:param num_bytes: number of bytes to random(select)
:param urandom: urandom function
:param to_hex: hexifying function
"""
# Compose.
if urandom is None:
urandom = os.urandom
if to_hex is None:
to_hex = hexlify

# Get random bytes.
r = urandom(num_bytes)

# Return as a unicode string.
return to_hex(r).decode("utf-8")


def ensure_unique(self, query, property_, value, max_iter=30, gen_digest=None):
def ensure_unique(self, query, property_, value, max_iter=30, gen_digest=generate_random_digest):
"""
Make sure slug is unique.
Takes a ``candidate`` value for a unique ``property_`` and iterates,
appending an incremented integer until unique.
"""
# Compose.
if gen_digest is None:
gen_digest = generate_random_digest

# Unpack
candidate = value

Expand Down
35 changes: 35 additions & 0 deletions tests/test_mixin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
from mock import Mock, patch

from pyramid_basemodel.mixin import TouchMixin


def test_touch_mixin():
"""Check wether every argument of TouchMixin get's called in proper order."""
t = TouchMixin()
saved_arg = []

def save_mock(instance):
saved_arg.append(instance)

assert not hasattr(t, "modified")
with patch.object(t, "propagate_touch") as propagate_mock:
t.touch(now=Mock, save=save_mock)
assert propagate_mock.called
assert hasattr(t, "modified")
assert t == saved_arg[0]


def test_touch_mixin_no_propagate():
"""Check wether every argument of TouchMixin get's called in proper order."""
t = TouchMixin()
saved_arg = []

def save_mock(instance):
saved_arg.append(instance)

assert not hasattr(t, "modified")
with patch.object(t, "propagate_touch") as propagate_mock:
t.touch(False, now=Mock, save=save_mock)
assert not propagate_mock.called
assert hasattr(t, "modified")
assert t == saved_arg[0]

0 comments on commit 61b6c39

Please sign in to comment.