Skip to content

Commit

Permalink
Merge pull request #1 from zopefoundation/master
Browse files Browse the repository at this point in the history
Fast forward to zopefoundation master
  • Loading branch information
Recursing committed Oct 20, 2020
2 parents 2e3afec + 83a052d commit 8057648
Show file tree
Hide file tree
Showing 17 changed files with 387 additions and 37 deletions.
9 changes: 6 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
*.pyc
__pycache__
src/*.egg-info

.coverage
.coverage.*
.installed.cfg
.tox/
*.pyc
bin
develop-eggs
htmlcov/
lib/
parts
pip-selfcheck.json
pyvenv.cfg
src/*.egg-info
5 changes: 3 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@ python:
- 3.4
- 3.5
- 3.6
- pypy-5.4.1
- pypy
- pypy3
install:
- pip install -U pip setuptools
- pip install -U coveralls coverage
- pip install -U zope.testrunner coveralls coverage
- pip install -U -e ".[test]"
script:
- coverage run -m zope.testrunner --test-path=src
Expand Down
23 changes: 20 additions & 3 deletions CHANGES.txt → CHANGES.rst
Original file line number Diff line number Diff line change
@@ -1,18 +1,35 @@
Changes
=======

2.0 (unreleased)
3.0.2 (unreleased)
------------------

- Nothing changed yet.


3.0.1 (2018-01-17)
------------------

- Replace the use of `grok.implements()` with the `@grok.implementer()`
directive throughout.

3.0.0 (2018-01-12)
------------------

- Rearrange tests such that Travis CI can pick up all functional tests too.

1.6 (2017-05-30)
----------------

- Drop support of Python 2.6 and claim support for Python 3.4, 3.5, 3.6 and PyPy.
- Add LazyAnnotation and LazyAnnotationProperty.

- Drop support of Python 2.6 and claim support for Python 3.4, 3.5, 3.6 and PyPy.

1.5.1 (2016-01-29)
------------------

- Update tests.


1.5 (2014-10-20)
----------------

Expand Down
14 changes: 7 additions & 7 deletions README.txt → README.rst
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@

This package provides a support to simplify the use of annotations in
Zope.

.. contents::


Setting up ``grokcore.annotation``
==================================

Expand All @@ -17,7 +15,6 @@ additional ZCML line you will need is::
Put this somewhere near the top of your root ZCML file but below the
line where you include ``grokcore.component``'s configuration.


Example
=======

Expand Down Expand Up @@ -55,8 +52,6 @@ Here a simple example of use of an annotation::
# on the annotation
livestock2.foo = "something"



API Overview
============

Expand All @@ -78,9 +73,14 @@ Base classes
``deleteAnnotation(model, interface)``
Look for the given annotation and delete it from the model.

``LazyAnnotation``
Base class for an annotation. It only writes a database object when
explicitly setting values on the lazy properties.

``LazyAnnotationProperty``
Property implementation that works with ``LazyAnnotation``.

In addition, the ``grokcore.annotation`` package exposes the
`grokcore.component`_ API.

.. _grokcore.component: http://pypi.python.org/pypi/grokcore.component


8 changes: 5 additions & 3 deletions buildout.cfg
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@
[buildout]
extends =
https://raw.githubusercontent.com/zopefoundation/groktoolkit/3.0.0a1/grok.cfg
develop = .
parts = interpreter test
extends = https://raw.githubusercontent.com/zopefoundation/groktoolkit/resurrection-python3/grok.cfg
versions = versions

[versions]
grokcore.annotation =
zope.testing = 4.6.1

[interpreter]
recipe = zc.recipe.egg
eggs = grokcore.annotation
interpreter = python
interpreter = py

[test]
recipe = zc.recipe.testrunner
eggs = grokcore.annotation
grokcore.annotation[test]
defaults = ['--tests-pattern', '^f?tests$', '-v']
defaults = ['-vc']
4 changes: 4 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# NOTE: setuptools and zc.buildout versions must be in sync with:
# ztk-versions.cfg
setuptools==38.2.4
zc.buildout==2.10.0
28 changes: 17 additions & 11 deletions setup.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,29 @@
from setuptools import setup, find_packages
import os

def read(*rnames):
return open(os.path.join(os.path.dirname(__file__), *rnames)).read()

def read(name):
"""Read a file."""
with open(name) as f:
return f.read()


long_description = (
read('README.txt')
read('README.rst')
+ '\n' +
read('CHANGES.txt')
)
read('CHANGES.rst')
)

tests_require = [
'zope.configuration',
'zope.schema',
'zope.testing > 4.6',
'zope.testrunner',
]
]


setup(
name='grokcore.annotation',
version='1.6.dev0',
version='3.0.2.dev0',
author='Grok Team',
author_email='grok-dev@zope.org',
url='http://grok.zope.org',
Expand All @@ -39,7 +43,7 @@ def read(*rnames):
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: Implementation :: CPython',
'Programming Language :: Python :: Implementation :: PyPy',
'Framework :: Zope3',
'Framework :: Zope :: 3',
],

packages=find_packages('src'),
Expand All @@ -52,11 +56,13 @@ def read(*rnames):
'martian',
'setuptools',
'zope.annotation',
'zope.cachedescriptors',
'zope.component',
'zope.container',
'zope.interface',
],
'zope.location',
],
tests_require=tests_require,
test_suite='grokcore.annotation.tests.test_grok.test_suite',
extras_require={'test': tests_require},
)
)
2 changes: 2 additions & 0 deletions src/grokcore/annotation/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
from grokcore.annotation.components import queryAnnotation
from grokcore.annotation.components import deleteAnnotation

from grokcore.annotation.lazy import LazyAnnotation, LazyAnnotationProperty

# BBB These two functions are meant for test fixtures and should be
# imported from grok.testing, not from grok.
from grokcore.annotation.testing import grok
Expand Down
9 changes: 8 additions & 1 deletion src/grokcore/annotation/interfaces.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,14 @@
class IBaseClasses(interface.Interface):
"""grokcore.annotation base classes.
"""
Annotation = interface.Attribute("Base class for persistent annotations.")
Annotation = interface.Attribute(
"Base class for persistent annotations.")

LazyAnnotation = interface.Attribute(
"Base class for lazily persisted annotations.")

LazyAnnotationProperty = interface.Attribute(
"Base class for schema attributes defined on lazy annotations.")


class IAnnotationFactory(interface.Interface):
Expand Down
116 changes: 116 additions & 0 deletions src/grokcore/annotation/lazy.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
##############################################################################
#
# Copyright (c) 2006-2007 Zope Foundation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Base classes for Grok application components.
"""

import persistent
import zope.annotation.interfaces
import zope.cachedescriptors.property

from zope.interface import implementer
from zope.location import Location
from zope.annotation.interfaces import IAnnotations
from grokcore.annotation.interfaces import IAnnotationFactory

_marker = object()


class LazyAnnotationProperty(object):

def __init__(self, field, name=None):
if name is None:
name = field.__name__

self.__field = field
self.__name = name

def __get__(self, inst, klass):
if not isinstance(inst, LazyAnnotation):
return self

value = inst._load(self.__name, _marker)
if value is _marker:
field = self.__field.bind(inst)
value = getattr(field, 'default', _marker)
if value is _marker:
raise AttributeError(self.__name)

return value

def __set__(self, inst, value):
if not isinstance(inst, LazyAnnotation):
raise ValueError(self.__name, 'invalid context')

field = self.__field.bind(inst)
if field.readonly:
raise ValueError(self.__name, 'field is readonly')
field.validate(value)
inst._store(self.__name, value)

def __getattr__(self, name):
return getattr(self.__field, name)


class Storage(persistent.Persistent):
pass


class LazyAnnotation(Location):

@zope.cachedescriptors.property.Lazy
def storage(self):
annotations = IAnnotations(self.__parent__)
return annotations.get(self.__name__)

def _store(self, key, value):
storage = self.storage
if storage is None:
annotations = IAnnotations(self.__parent__)
annotations[self.__name__] = storage = Storage()
self.__dict__['storage'] = storage
setattr(storage, key, value)

def _load(self, key, default):
storage = self.storage
if storage is None:
return default
return getattr(storage, key, default)


@implementer(IAnnotationFactory)
class LazyAnnotationFactory(object):

def __init__(self, factory, name):
self.factory = factory
self.name = name

def query(self, context):
annotations = IAnnotations(context)
if self.name in annotations:
return self(context)
return None

def delete(self, context):
annotations = IAnnotations(context)
if self.name in annotations:
del annotations[self.name]
return True
return False

def __call__(self, context):
result = self.factory()
result.__parent__ = context
result.__name__ = self.name
return result
21 changes: 21 additions & 0 deletions src/grokcore/annotation/meta.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"""
from martian import util
from grokcore.annotation.components import AnnotationFactory
from grokcore.annotation.lazy import LazyAnnotationFactory
from zope.interface import implementedBy
import grokcore.annotation
import martian
Expand Down Expand Up @@ -50,3 +51,23 @@ def execute(self, factory, config, adapter_context, provides, name, **kw):
args=(factory, (adapter_context,), provides, ''),
)
return True


class LazyAnnotationGrokker(martian.ClassGrokker):
"""Grokker for components subclassed from `grok.LazyAnnotation`.
"""
martian.component(grokcore.annotation.LazyAnnotation)
martian.directive(grokcore.annotation.context, name='adapter_context')
martian.directive(grokcore.annotation.provides,
get_default=default_annotation_provides)
martian.directive(grokcore.annotation.name,
get_default=default_annotation_name)

def execute(self, factory, config, adapter_context, provides, name, **kw):
factory = LazyAnnotationFactory(factory, name)
config.action(
discriminator=('adapter', adapter_context, provides, ''),
callable=grokcore.component.provideAdapter,
args=(factory, (adapter_context,), provides, ''),
)
return True
2 changes: 1 addition & 1 deletion src/grokcore/annotation/tests/annotation/annotation.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,8 @@ def addBrand(brand):
def getBrands():
"""Return a list of brands."""

@grok.implementer(IBranding)
class Branding(grok.Annotation):
grok.implements(IBranding)

def __init__(self):
self._brands = OOTreeSet()
Expand Down
Loading

0 comments on commit 8057648

Please sign in to comment.