Skip to content

Commit

Permalink
add get_addon_metadata provisional public api
Browse files Browse the repository at this point in the history
  • Loading branch information
sbidoul committed Aug 19, 2019
1 parent f6360dd commit 11262bf
Show file tree
Hide file tree
Showing 10 changed files with 172 additions and 59 deletions.
8 changes: 5 additions & 3 deletions CHANGES.rst
Expand Up @@ -7,9 +7,11 @@ Changes
2.5.0 (unreleased)
------------------
- [IMP] new git autoversioning strategy (increase last digit instead of
appending .99)
- [IMP] preliminary Odoo 13 support
- [ADD] new git autoversioning strategy (increase last digit instead of
appending .99), will be used for Odoo 13
- [ADD] preliminary Odoo 13 support
- [ADD] new provisional public API that returns Python Package Metada 2.1 for
and Odoo addon

2.4.1 (2018-11-05)
------------------
Expand Down
9 changes: 5 additions & 4 deletions README.rst
Expand Up @@ -384,13 +384,14 @@ and x.y.z.devN is considered anterior to x.y.z.).
for pip to install a developmental version, it must be invoked with the --pre
option.

Helper API
Public API
~~~~~~~~~~

.. Note:: TODO
The ``setuptools_odoo`` package exposes a provisional public API.

Should you have a use case for using the setuptools-odoo internals,
get in touch so we can review your needs and expose a clean API.
* ``get_addon_metadata(addon_dir, ...)`` returns an ``email.message.Message``
compliant with `PEP 566 -- Metadata for Python Software Packages 2.1
<https://www.python.org/dev/peps/pep-0566/>`_.

Useful links
~~~~~~~~~~~~
Expand Down
14 changes: 2 additions & 12 deletions setuptools_odoo/__init__.py
Expand Up @@ -4,20 +4,10 @@


from .core import (
prepare_odoo_addon,
prepare_odoo_addons,
get_install_requires_odoo_addon,
get_install_requires_odoo_addons,
make_pkg_name,
make_pkg_requirement,
get_addon_metadata
)


__all__ = [
prepare_odoo_addon.__name__,
prepare_odoo_addons.__name__,
get_install_requires_odoo_addon.__name__,
get_install_requires_odoo_addons.__name__,
make_pkg_name.__name__,
make_pkg_requirement.__name__,
get_addon_metadata.__name__
]
117 changes: 92 additions & 25 deletions setuptools_odoo/core.py
Expand Up @@ -2,6 +2,7 @@
# Copyright © 2015-2018 ACSONE SA/NV
# License LGPLv3 (http://www.gnu.org/licenses/lgpl-3.0-standalone.html)

from email.message import Message
import email.parser
import io
import os
Expand Down Expand Up @@ -140,8 +141,15 @@ def _get_version(addon_dir, manifest, odoo_version_override=None,
return version, odoo_version, odoo_version_info


def _no_nl(s):
if not s:
return s
return " ".join(s.split())


def _get_description(addon_dir, manifest):
return manifest.get('summary', '').strip() or manifest.get('name').strip()
s = manifest.get('summary', '').strip() or manifest.get('name').strip()
return _no_nl(s)


def _get_long_description(addon_dir, manifest):
Expand All @@ -154,7 +162,7 @@ def _get_long_description(addon_dir, manifest):


def _get_author(manifest):
return manifest.get('author')
return _no_nl(manifest.get('author'))


def _get_author_email(manifest):
Expand Down Expand Up @@ -345,29 +353,57 @@ def _setuptools_find_packages(odoo_version_info):
return pkg


def prepare_odoo_addon(depends_override={},
external_dependencies_override={},
odoo_version_override=None):
addons_dir, addons_ns = _find_addons_dir()
potential_addons = os.listdir(addons_dir)
# list installable addons, except auto-installable ones
# in case we want to combine an addon and it's glue modules
# in a package named after the main addon
addons = [a for a in potential_addons
if is_installable_addon(os.path.join(addons_dir, a),
unless_auto_installable=True)]
if len(addons) == 0:
# if no addon is found, it may mean we are trying to package
# a single module that is marked auto-install, so let's try
# listing all installable modules
addons = [a for a in potential_addons
if is_installable_addon(os.path.join(addons_dir, a))]
if len(addons) != 1:
raise DistutilsSetupError('%s must contain exactly one '
'installable Odoo addon dir, found %s' %
(os.path.abspath(addons_dir), addons))
addon_name = addons[0]
addon_dir = os.path.join(addons_dir, addon_name)
def get_addon_metadata(
addon_dir,
depends_override={},
external_dependencies_override={},
odoo_version_override=None,
):
"""
Return Python Package Metadata 2.1 for an Odoo addon as an
email.message.Message.
The Description field is absent and is stored in the message payload.
All values are guaranteed to not contain newline characters, except for
the payload. On python 3, all values are str. On python 2 the value types
can be str or unicode depending on the python manifests.
"""
smeta = get_addon_setuptools_keywords(addon_dir)
meta = Message()

def _set(name, sname):
svalue = smeta.get(sname)
if svalue:
if not isinstance(svalue, list):
svalue = [svalue]
for item in svalue:
meta[name] = item

meta["Metadata-Version"] = "2.1"
_set("Name", "name")
_set("Version", "version")
_set("Requires-Python", "python_requires")
_set("Requires-Dist", "install_requires")
_set("Summary", "description")
_set("Home-page", "url")
_set("License", "license")
_set("Author", "author")
_set("Author-email", "author_email")
_set("Classifier", "classifiers")
long_description = smeta.get("long_description")
if long_description:
meta.set_payload(long_description)

return meta


def get_addon_setuptools_keywords(
addon_dir,
depends_override={},
external_dependencies_override={},
odoo_version_override=None
):
addon_name = os.path.basename(addon_dir)
manifest = read_manifest(addon_dir)
if os.path.exists('PKG-INFO'):
with io.open('PKG-INFO', encoding='utf-8') as fp:
Expand Down Expand Up @@ -405,6 +441,37 @@ def prepare_odoo_addon(depends_override={},
return {k: v for k, v in setup_keywords.items() if v is not None}


def prepare_odoo_addon(depends_override={},
external_dependencies_override={},
odoo_version_override=None):
addons_dir, addons_ns = _find_addons_dir()
potential_addons = os.listdir(addons_dir)
# list installable addons, except auto-installable ones
# in case we want to combine an addon and it's glue modules
# in a package named after the main addon
addons = [a for a in potential_addons
if is_installable_addon(os.path.join(addons_dir, a),
unless_auto_installable=True)]
if len(addons) == 0:
# if no addon is found, it may mean we are trying to package
# a single module that is marked auto-install, so let's try
# listing all installable modules
addons = [a for a in potential_addons
if is_installable_addon(os.path.join(addons_dir, a))]
if len(addons) != 1:
raise DistutilsSetupError('%s must contain exactly one '
'installable Odoo addon dir, found %s' %
(os.path.abspath(addons_dir), addons))
addon_name = addons[0]
addon_dir = os.path.join(addons_dir, addon_name)
return get_addon_setuptools_keywords(
addon_dir,
depends_override,
external_dependencies_override,
odoo_version_override,
)


def prepare_odoo_addons(depends_override={},
external_dependencies_override={},
odoo_version_override=None):
Expand Down
5 changes: 4 additions & 1 deletion tests/data/addon1/__openerp__.py
@@ -1,6 +1,9 @@
{
'name': 'addon 1',
'summary': 'addon 1 summary',
'summary': '''
addon 1
summary
''',
'version': '8.0.1.0.0',
'author': 'ACSONE SA/NV, '
'Odoo Community Association (OCA)',
Expand Down
48 changes: 48 additions & 0 deletions tests/test_get_addon_metadata.py
@@ -0,0 +1,48 @@
# -*- coding: utf-8 -*-
# Copyright © 2019 ACSONE SA/NV
# License LGPLv3 (http://www.gnu.org/licenses/lgpl-3.0-standalone.html)

import os
try:
from itertools import zip_longest
except ImportError: # py27
from itertools import izip_longest as zip_longest

from setuptools_odoo import get_addon_metadata

from . import DATA_DIR


def _assert_msg(msg, expected_items, expected_payload=None):
for msg_item, expected_item in zip_longest(msg.items(), expected_items):
assert msg_item == expected_item
if expected_payload:
assert msg.get_payload() == expected_payload


def test_addon1():
addon_dir = os.path.join(DATA_DIR, "addon1")
metadata = get_addon_metadata(addon_dir)
_assert_msg(
metadata,
[
("Metadata-Version", "2.1"),
("Name", "odoo8-addon-addon1"),
("Version", "8.0.1.0.0.99.dev4"),
("Requires-Python", "~=2.7"),
("Requires-Dist", "odoo>=8.0a,<9.0a"),
("Summary", "addon 1 summary"),
("Home-page", "https://acsone.eu/"),
("License", "AGPL-3"),
("Author", "ACSONE SA/NV, Odoo Community Association (OCA)"),
("Author-email", "support@odoo-community.org"),
("Classifier", "Programming Language :: Python"),
("Classifier", "Framework :: Odoo"),
(
"Classifier",
"License :: OSI Approved :: "
"GNU Affero General Public License v3",
),
("Classifier", "Development Status :: 4 - Beta"),
],
)
10 changes: 5 additions & 5 deletions tests/test_git_postversion.py
Expand Up @@ -16,12 +16,12 @@


def test_addon1():
""" addon1 has 2 commit after version 8.0.1.0.0 """
""" addon1 has 4 commit after version 8.0.1.0.0 """
addon1_dir = os.path.join(DATA_DIR, 'addon1')
version = get_git_postversion(addon1_dir, STRATEGY_99_DEVN)
assert version == '8.0.1.0.0.99.dev3'
assert version == '8.0.1.0.0.99.dev4'
version = get_git_postversion(addon1_dir, STRATEGY_P1_DEVN)
assert version == '8.0.1.0.1.dev3'
assert version == '8.0.1.0.1.dev4'


def test_addon2():
Expand Down Expand Up @@ -61,9 +61,9 @@ def test_addon1_uncommitted_change():
with open(manifest_path, "w") as f:
f.write(manifest.replace("summary", "great summary"))
version = get_git_postversion(addon1_dir, STRATEGY_99_DEVN)
assert version == '8.0.1.0.0.99.dev4'
assert version == '8.0.1.0.0.99.dev5'
version = get_git_postversion(addon1_dir, STRATEGY_P1_DEVN)
assert version == '8.0.1.0.1.dev4'
assert version == '8.0.1.0.1.dev5'
finally:
with open(manifest_path, "w") as f:
f.write(manifest)
Expand Down
10 changes: 6 additions & 4 deletions tests/test_install_requires.py
Expand Up @@ -4,7 +4,9 @@
import os
import unittest

import setuptools_odoo
from setuptools_odoo.core import (
get_install_requires_odoo_addon, get_install_requires_odoo_addons
)

from . import DATA_DIR

Expand All @@ -13,7 +15,7 @@ class TestInstallRequires(unittest.TestCase):
""" Test the install_requires... public api """

def test_addons_dir(self):
r = setuptools_odoo.get_install_requires_odoo_addons(DATA_DIR)
r = get_install_requires_odoo_addons(DATA_DIR)
self.assertEqual(set(r), set([
'astropy',
# we have a mix of addons version, so two versions of Odoo
Expand All @@ -26,12 +28,12 @@ def test_addons_dir(self):

def test_addon1(self):
addon_dir = os.path.join(DATA_DIR, 'addon1')
r = setuptools_odoo.get_install_requires_odoo_addon(addon_dir)
r = get_install_requires_odoo_addon(addon_dir)
self.assertEqual(r, ['odoo>=8.0a,<9.0a'])

def test_addon2(self):
addon_dir = os.path.join(DATA_DIR, 'addon2')
r = setuptools_odoo.get_install_requires_odoo_addon(addon_dir)
r = get_install_requires_odoo_addon(addon_dir)
self.assertEqual(r, ['odoo8-addon-addon1',
'odoo>=8.0a,<9.0a',
'python-dateutil'])
Expand Down
2 changes: 1 addition & 1 deletion tests/test_prepare.py
Expand Up @@ -50,7 +50,7 @@ def test_addon1():
'namespace_packages': ['odoo_addons'],
'packages': ['odoo_addons'],
'url': 'https://acsone.eu/',
'version': '8.0.1.0.0.99.dev3',
'version': '8.0.1.0.0.99.dev4',
'zip_safe': False,
}

Expand Down
8 changes: 4 additions & 4 deletions tests/test_setup_keywords.py
Expand Up @@ -34,7 +34,7 @@ def test_odoo_addon1(self):
[pkg_resources.Requirement.parse(r) for r in
['odoo>=8.0a,<9.0a']])
self.assertTrue(dist.has_metadata('not-zip-safe'))
self.assertEqual(dist.version, "8.0.1.0.0.99.dev3")
self.assertEqual(dist.version, "8.0.1.0.0.99.dev4")
finally:
shutil.rmtree(egg_info_dir)

Expand All @@ -48,21 +48,21 @@ def test_odoo_addon1_sdist(self):
], cwd=addon1_dir)
sdist_file = os.path.join(
dist_dir,
'odoo8-addon-addon1-8.0.1.0.0.99.dev3.tar.gz')
'odoo8-addon-addon1-8.0.1.0.0.99.dev4.tar.gz')
assert os.path.isfile(sdist_file)
# dist from the tar file, must produce an identical tar file
with tarfile.open(sdist_file, 'r') as tf:
tar_dir = tempfile.mkdtemp()
try:
tar_setup_dir = os.path.join(
tar_dir, 'odoo8-addon-addon1-8.0.1.0.0.99.dev3')
tar_dir, 'odoo8-addon-addon1-8.0.1.0.0.99.dev4')
tf.extractall(tar_dir)
subprocess.check_call([
sys.executable, 'setup.py', 'sdist',
], cwd=tar_setup_dir)
sdist_file2 = os.path.join(
tar_setup_dir, 'dist',
'odoo8-addon-addon1-8.0.1.0.0.99.dev3.tar.gz')
'odoo8-addon-addon1-8.0.1.0.0.99.dev4.tar.gz')
assert os.path.isfile(sdist_file2)
with tarfile.open(sdist_file2, 'r') as tf2:
assert sorted(tf.getnames()) == sorted(tf2.getnames())
Expand Down

0 comments on commit 11262bf

Please sign in to comment.