launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #07563
[Merge] lp:~allenap/maas/convert-enums-redux into lp:maas
Gavin Panella has proposed merging lp:~allenap/maas/convert-enums-redux into lp:maas.
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
For more details, see:
https://code.launchpad.net/~allenap/maas/convert-enums-redux/+merge/104568
Depending on "enums" - a phony target - in Makefile was making some things get rebuilt again and again. Instead I changed it to depend on the target file instead, which make is much happier resolving... and then I got my yak attack on.
I also changed convert-enums.py to accept a list of file names, instead of repeating the work that make had just done to find the modules. I also moved it into the src/ tree and added tests for the more interesting functions.
--
https://code.launchpad.net/~allenap/maas/convert-enums-redux/+merge/104568
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~allenap/maas/convert-enums-redux into lp:maas.
=== modified file 'Makefile'
--- Makefile 2012-05-02 03:58:51 +0000
+++ Makefile 2012-05-03 14:56:26 +0000
@@ -1,4 +1,9 @@
-PYTHON = python2.7
+python := python2.7
+
+# Python enum modules.
+py_enums := $(wildcard src/*/enum.py)
+# JavaScript enum module (not modules).
+js_enums := src/maasserver/static/js/enums.js
build: \
bin/buildout \
@@ -6,19 +11,19 @@
bin/twistd.pserv bin/test.pserv \
bin/twistd.txlongpoll \
bin/py bin/ipy \
- enums
+ $(js_enums)
all: build doc
bin/buildout: bootstrap.py distribute_setup.py
- $(PYTHON) bootstrap.py --distribute --setup-source distribute_setup.py
+ $(python) bootstrap.py --distribute --setup-source distribute_setup.py
@touch --no-create $@ # Ensure it's newer than its dependencies.
-bin/maas: bin/buildout buildout.cfg versions.cfg setup.py enums
+bin/maas: bin/buildout buildout.cfg versions.cfg setup.py $(js_enums)
bin/buildout install maas
@touch --no-create $@
-bin/test.maas: bin/buildout buildout.cfg versions.cfg setup.py enums
+bin/test.maas: bin/buildout buildout.cfg versions.cfg setup.py $(js_enums)
bin/buildout install maas-test
@touch --no-create $@
@@ -49,7 +54,7 @@
dev-db:
utilities/maasdb start ./db/ disposable
-test: bin/test.maas bin/test.pserv enums
+test: bin/test.maas bin/test.pserv $(js_enums)
bin/test.maas
bin/test.pserv
@@ -80,20 +85,16 @@
doc: bin/sphinx docs/api.rst
bin/sphinx
-# JavaScript enums module, generated from python enums modules.
-JSENUMS = src/maasserver/static/js/enums.js
-
-# Generate JavaScript enums from python enums.
-enums: $(JSENUMS)
-
-$(JSENUMS): utilities/convert-enums.py src/*/enum.py
- utilities/convert-enums.py --src=src >$@
+enums: $(js_enums)
+
+$(js_enums): bin/py src/maas/utils/jsenums.py $(py_enums)
+ bin/py -m src/maas/utils/jsenums $(py_enums) > $@
clean:
find . -type f -name '*.py[co]' -print0 | xargs -r0 $(RM)
find . -type f -name '*~' -print0 | xargs -r0 $(RM)
$(RM) -r media/demo/* media/development
- $(RM) $(JSENUMS)
+ $(RM) $(js_enums)
distclean: clean stop
utilities/maasdb delete-cluster ./db/
=== added directory 'src/maas/utils'
=== added file 'src/maas/utils/__init__.py'
=== renamed file 'utilities/convert-enums.py' => 'src/maas/utils/jsenums.py'
--- utilities/convert-enums.py 2012-05-02 17:13:01 +0000
+++ src/maas/utils/jsenums.py 2012-05-03 14:56:26 +0000
@@ -8,8 +8,9 @@
Running this script produces a source text containing the JavaScript
equivalents of the same enums, so that JavaScript code can make use of them.
-The script takes one option: --src=DIRECTORY. DIRECTORY is where the MAAS
-modules (maasserver, metadataserver, provisioningserver) can be found.
+The script takes the filename of the enum modules. Each will be compiled and
+executed in an empty namespace, though they will have access to other MAAS
+libraries, though not necessarily their dependencies.
The resulting JavaScript module is printed to standard output.
"""
@@ -21,14 +22,18 @@
)
__metaclass__ = type
+__all__ = []
from argparse import ArgumentParser
from datetime import datetime
+from itertools import chain
import json
-import os.path
+from operator import attrgetter
import sys
from textwrap import dedent
+from maasserver.utils import map_enum
+
# Header. Will be written on top of the output.
header = dedent("""\
/*
@@ -44,43 +49,10 @@
"""
% {'script': sys.argv[0], 'timestamp': datetime.now()})
-
# Footer. Will be written at the bottom.
footer = "}, '0.1');"
-def get_module(src_path, package):
- """Attempt to load a given module.
-
- This makes some assumptions about directory structure: it is assumed
- that the module is directly in a package of the given name, which in turn
- should be directly in the search path.
-
- :param src_path: The path to search in.
- :param package: The package to load the requested module from.
- :param name: Name of module to load.
- :return: The imported module, or None if it was not found.
- """
- if os.path.isfile(os.path.join(src_path, package, "enum.py")):
- return __import__('.'.join([package, 'enum']), level=0, fromlist=True)
- else:
- return None
-
-
-def find_enum_modules(src_path):
- """Find MAAS "enum" modules in the packages in src_path.
-
- This assumes that all MAAS enums can be found in
- <src_path>/<package>/enum.py.
-
- :param src_path: The path to search in.
- :return: An iterable of "enum" modules found in packages in src_path.
- """
- dirs = sorted(os.listdir(src_path))
- modules = [get_module(src_path, package) for package in dirs]
- return filter(None, modules)
-
-
def is_enum(item):
"""Does the given python item look like an enum?
@@ -90,16 +62,22 @@
return isinstance(item, type) and item.__name__.isupper()
-def get_enum_classes(module):
- """Collect all enum classes exported from loaded `module`."""
- return filter(is_enum, [module.__dict__[name] for name in module.__all__])
+def get_enum_classes(namespace):
+ """Collect all enum classes exported from `namespace`."""
+ return filter(is_enum, namespace.values())
+
+
+def get_enums(filename):
+ namespace = {}
+ with open(filename, "rbU") as fd:
+ source = fd.read()
+ code = compile(source, filename, "exec")
+ exec(code, namespace)
+ return get_enum_classes(namespace)
def serialize_enum(enum):
"""Represent a MAAS enum class in JavaScript."""
- # Import lazily to make use of initialized path.
- from maasserver.utils import map_enum
-
return "module.%s = %s;\n" % (
enum.__name__,
json.dumps(map_enum(enum), indent=4, sort_keys=True))
@@ -107,24 +85,22 @@
def parse_args():
"""Parse options & arguments."""
- default_src = os.path.join(os.path.dirname(sys.path[0]), 'src')
parser = ArgumentParser(
"Generate JavaScript enums based on python enums modules")
parser.add_argument(
- '--src', metavar='SOURCE_DIRECTORY', type=str, default=default_src,
- help="Look for the MAAS packages in SOURCE_DIRECTORY.")
+ 'sources', metavar="FILENAME", nargs='+',
+ help="File to search for enums.")
return parser.parse_args()
-def main(args):
- enum_modules = find_enum_modules(args.src)
- enums = sum((get_enum_classes(module) for module in enum_modules), [])
+def dump(source_filenames):
+ enums = chain.from_iterable(
+ get_enums(filename) for filename in source_filenames)
+ enums = sorted(enums, key=attrgetter("__name__"))
dumps = [serialize_enum(enum) for enum in enums]
- print("\n".join([header] + dumps + [footer]))
+ return "\n".join([header] + dumps + [footer])
if __name__ == "__main__":
args = parse_args()
- # Add src directory so that we can import from MAAS packages.
- sys.path.append(args.src)
- main(args)
+ print(dump(args.sources))
=== added directory 'src/maas/utils/tests'
=== added file 'src/maas/utils/tests/__init__.py'
=== added file 'src/maas/utils/tests/test_jsenums.py'
--- src/maas/utils/tests/test_jsenums.py 1970-01-01 00:00:00 +0000
+++ src/maas/utils/tests/test_jsenums.py 2012-05-03 14:56:26 +0000
@@ -0,0 +1,59 @@
+# Copyright 2012 Canonical Ltd. This software is licensed under the
+# GNU Affero General Public License version 3 (see the file LICENSE).
+
+"""Tests for `maas.utils.jsenums`."""
+
+from __future__ import (
+ absolute_import,
+ print_function,
+ unicode_literals,
+ )
+
+__metaclass__ = type
+__all__ = []
+
+from maas.utils.jsenums import (
+ dump,
+ footer,
+ get_enums,
+ header,
+ serialize_enum,
+ )
+from maasserver.utils import map_enum
+from maastesting.testcase import TestCase
+
+
+class ENUM:
+ ALICE = 1
+ BOB = 2
+
+
+class Test(TestCase):
+
+ def test_serialize_enum(self):
+ # The name is used correctly, the keys are sorted, and everything is
+ # indented correctly.
+ self.assertEqual(
+ u'module.ENUM = {\n'
+ u' "ALICE": 1, \n'
+ u' "BOB": 2\n'
+ u'};\n',
+ serialize_enum(ENUM))
+
+ def test_get_enums(self):
+ # This file contains a single enum, named "ENUM".
+ enums = get_enums(__file__)
+ self.assertEqual(["ENUM"], [enum.__name__ for enum in enums])
+ [enum] = enums
+ # Because the module has been executed in a different namespace, the
+ # enum we've found is not the same object as the one in the current
+ # global namespace.
+ self.assertIsNot(ENUM, enum)
+ # It does, however, have the same values.
+ self.assertEqual(map_enum(ENUM), map_enum(enum))
+
+ def test_dump(self):
+ self.assertEqual(header + "\n" + footer, dump([]))
+ self.assertEqual(
+ header + "\n" + serialize_enum(ENUM) + "\n" + footer,
+ dump([__file__]))