Skip to content
This repository has been archived by the owner on Feb 13, 2024. It is now read-only.

Initial Release - foreach API #1

Merged
merged 3 commits into from
Oct 25, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions .codeclimate.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
languages:
JavaScript: false
Python: true
28 changes: 28 additions & 0 deletions .coveragerc
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
[run]

branch = True

[report]

exclude_lines =
pragma: no cover
def __repr__
if self.debug:
raise NotImplementedError
if __name__ == .__main__.:

ignore_errors = True

include =
*/oca/*

omit =
*/virtualenv/*
*/tests/*
setup.py
*/__init__.py
tests.py

[xml]

output = coverage.xml
92 changes: 92 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class

# C extensions
*.so

# Distribution / packaging
.Python
env/
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
*.egg-info/
.installed.cfg
*.egg

# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec

# Installer logs
pip-log.txt
pip-delete-this-directory.txt

# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*,cover
.hypothesis/

# Translations
*.mo
*.pot

# Django stuff:
*.log
local_settings.py

# Flask stuff:
instance/
.webassets-cache

# Scrapy stuff:
.scrapy

# Sphinx documentation
docs/_build/

# PyBuilder
target/

# IPython Notebook
.ipynb_checkpoints

# pyenv
.python-version

# celery beat schedule file
celerybeat-schedule

# dotenv
.env

# virtualenv
venv/
ENV/

# Spyder project settings
.spyderproject

# Rope project settings
.ropeproject

# PyCharm
.idea/
33 changes: 33 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
cache: pip

language: python

python:
- "2.7"
- "3.5"
- "3.6"

install:
- pip install tox-travis
- pip install codecov
- pip install codeclimate-test-reporter

script:
- tox

after_success:
- codecov
- codeclimate-test-reporter

deploy:
provider: pypi
user: laslabs
password:
secure: "UOFAfNJj1NNP3qI73IzrTncleyxSHwnYVpRe5vgAzlg0ojZWFTPRzac9JxKOvZPViuPnS+v8/UOequEz7sEOwLnYQIZcW1k4TAYWSNeAhjn7MpcboaSsujgGE1eMYWor3pgnU6f0EvBp0bhgfxciwHhjfU6PSr2CAfmJDjT02G1lsk8N94gTSYNwy+FV3QRPWlsB+ZyGGoUwf+Ap0gh7U3EaFSeVZ8P+y8oP99A1Zyz4dd0GjW2/5zj4eXm6boDUGt8sPAATf0Lni3xMfE/T+43MfMYRj1Yw6Ho7+mgYLL+RpgrY7pHA3pfOTomoz11yHnNYZq3sCKjDclspq64XSryICGOYC9hDT5vz+GbFzbpcnimbitdhO8STLPK9O0AMbrbanPaHVtNpC7dA+ZKpXtkD2UYN/GUVcc//nDCkGs6T1bFHFV9NUKfEqLr6LpS6Yb9MLc0g+x3b1+632FfoZ5+8uxM+NKHgh6uos9DPiLx8ZOCfgqVUqXexJq21bFuELqMkHZEu01Pe2MIB5K5ol3KDETYTYJJq4XF6+kF4Ge64LV+iRCP9WVxnX8OtovYxE5/56lB9S7Qc57Y1PpVupUGCo67wVE0UP7LHcs7qfEKmS/ulr2ARYNBVO0vfWrWm+9EnP/HNwqok4cna2/voVgoXNFeAj4u8+v2TfvKfIGg="
distributions: "sdist bdist_wheel"
skip_upload_docs: true
on:
repo: oca/oca-decorators
branch: master
tags: true

80 changes: 80 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
|License LGPL-3| | |PyPi Package| | |PyPi Versions|

|Build Status| | |Test Coverage| | |Code Climate|

==========
Python OCA
==========

This library contains helpers for Odoo developers, such as:

* ``oca.decorators.foreach`` - A replacement for the deprecated ``odoo.api.one``

Installation
============

Install this package into your Odoo environment.

Using Pip:

pip install oca-decorators

Using Git:

git clone https://github.com/OCA/oca-decorators
python oca-decorators/setup.py install


Setup
=====

Usage
=====

* `Read The API Documentation <https://oca.github.io/oca-decorators>`_

Known Issues / Road Map
=======================


Credits
=======

Images
------

Contributors
------------

* Dave Lasley <dave@laslabs.com>

Maintainer
----------

.. image:: https://odoo-community.org/logo.png
:alt: Odoo Community Association
:target: https://odoo-community.org

This library is maintained by the OCA.

OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.

To contribute to this module, please visit https://odoo-community.org.

.. |Build Status| image:: https://img.shields.io/travis/OCA/oca-decorators/master.svg
:target: https://travis-ci.org/OCA/oca-decorators
.. |Test Coverage| image:: https://img.shields.io/codecov/c/github/OCA/oca-decorators/master.svg
:target: https://codecov.io/gh/OCA/oca-decorators
.. |Code Climate| image:: https://img.shields.io/codeclimate/github/OCA/oca-decorators.svg
:target: https://codeclimate.com/github/OCA/oca-decorators
.. |License LGPL-3| image:: https://img.shields.io/github/license/OCA/oca-decorators.svg
:target: http://www.gnu.org/licenses/lgpl
:alt: License: LGPL-3
.. |PyPi Package| image:: https://img.shields.io/pypi/v/oca-decorators.svg
:target: https://pypi.python.org/pypi/oca-decorators
:alt: PyPi Package
.. |PyPi Versions| image:: https://img.shields.io/pypi/pyversions/oca-decorators.svg
:target: https://pypi.python.org/pypi/oca-decorators
:alt: PyPi Versions
5 changes: 5 additions & 0 deletions oca/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# -*- coding: utf-8 -*-
# Copyright 2016 LasLabs Inc.
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).

Copy link
Sponsor Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@lasley This file must only contain __path__ = __import__('pkgutil').extend_path(__path__, __name__) to keep oca as a reusable namespace package.
https://packaging.python.org/guides/packaging-namespace-packages/#pkgutil-style-namespace-packages

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the complexity of having the package split doesn't pay. It's just easier to have all in one place, this shouldn't grow up so much as to be unmaintainable. Just a collection of helpers...

Copy link
Sponsor Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@yajo I think we must keep the possibility to release others python packages under the oca namespace. (ie: utilities to use in our dev/release process, ????). There is nothing complex here.

__path__ = __import__('pkgutil').extend_path(__path__, __name__)
10 changes: 10 additions & 0 deletions oca/decorators/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
# Copyright 2016 LasLabs Inc.
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).

from .foreach import foreach


__all__ = [
Copy link
Sponsor Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@lasley You can leave this file empty.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is required in order to make the foreach method inside of the foreach file directly importable.

Otherwise usage is:

from oca.decorators.foreach import foreach

instead of

from oca.decorators import foreach

Do you have a better way to solve this?

Copy link
Sponsor Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we really want one file per decorator? 😲

Copy link
Contributor Author

@lasley lasley Oct 19, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I generally prefer deeper file hierarchies with smaller files. I've found that if I don't do this type of thing from the get-go I end up in a situation where the files are thousands of lines deep, I spend a bunch of time trying to categorize things, then just lose stuff anyways.

I'm open to consideration here though. You would prefer to categorize/group somehow?

Copy link
Sponsor Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@lasley IMO Your way of organizing your code is safe.

'foreach',
]
76 changes: 76 additions & 0 deletions oca/decorators/foreach.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# -*- coding: utf-8 -*-
# Copyright 2004-2015 Odoo S.A.
# Copyright 2016-2017 LasLabs Inc.
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).

from decorator import decorator


def foreach(container=None):
""" Loop the decorated method and return the results in a new `container`.

Example:

.. code-block:: python

@helpers.foreach(list)
def method(self):
return self.id

If the above method is called on a Record Singleton with the ``id`` 1
it would return:

[1]

If the method is called on a RecordSet containing Records with ``id``
1, 2, and 3 it would return:

[1, 2, 3]

If no container is provided as an argument to the decorator, and a
value is returned from the decorated method, a ValueError will be
raised.

.. code-block:: python

@helpers.foreach()
def method(self):
# Raises ValueError
return 1+2

@helpers.foreach()
def method(self):
# No error raised
self.math = 1+2

Args:
container (callable): It will be called with an iterable of the
decorated method results, then returned.

Raises:
AssertionError: If no container type is provided, and a value is
returned from the decorated method. This will only be raised if
``PYTHONOPTIMIZE`` is less than 2.
TypeError: If an incorrect data type is provided as `container`.
Valid arguments are `None` or any callable.

Returns:
mixed: A new `container`, instantiated with the results of the
decorated method - iterated so that `self` is a singleton.
"""

def _foreach(method):

def loop(method, self, *args, **kwargs):
results = (method(rec, *args, **kwargs) for rec in self)
if container is None:
for result in results:
assert result is None
else:
return container(results)

wrapper = decorator(loop, method)
wrapper._helpers = 'foreach'
return wrapper

return _foreach
3 changes: 3 additions & 0 deletions oca/tests/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# -*- coding: utf-8 -*-
# Copyright 2016 LasLabs Inc.
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
15 changes: 15 additions & 0 deletions oca/tests/common.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# -*- coding: utf-8 -*-
# Copyright 2016-2017 LasLabs Inc.
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).


class TestRecordset(object):
""" It provides a mock RecordSet for testing """

records = ['test1', 'test2']
iter_count = 0

def __iter__(self):
for record in self.records:
yield record
self.iter_count += 1