Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ pip-log.txt

# Unit test / coverage reports
.coverage
.cache
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

This was created by unit tests in Python 3.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

👍


# Translations
*.mo
Expand Down
17 changes: 17 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
language: python

sudo: required
dist: trusty

python:
- 2.7
- 3.6

install:
- pip install pytest coveralls

script:
- PYTHONPATH=$(pwd)/source pytest test

after_success:
- coveralls
150 changes: 27 additions & 123 deletions source/lucidity/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,127 +2,31 @@
# :copyright: Copyright (c) 2013 Martin Pengelly-Phillips
# :license: See LICENSE.txt.

import os
import uuid
import imp

from ._version import __version__
from .template import Template, Resolver
from .error import ParseError, FormatError, NotFound


def discover_templates(paths=None, recursive=True):
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

These were moved into template.py for consistency.

Copy link
Copy Markdown
Contributor

@martinpengellyphillips martinpengellyphillips May 3, 2017

Choose a reason for hiding this comment

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

I actually had these in __init__.py as the 'high level' API that operates on template instances rather than being templates themselves.

So I don't really see a benefit to changing this at this stage.

'''Search *paths* for mount points and load templates from them.

*paths* should be a list of filesystem paths to search for mount points.
If not specified will try to use value from environment variable
:envvar:`LUCIDITY_TEMPLATE_PATH`.

A mount point is a Python file that defines a 'register' function. The
function should return a list of instantiated
:py:class:`~lucidity.template.Template` objects.

If *recursive* is True (the default) then all directories under a path
will also be searched.

'''
templates = []

if paths is None:
paths = os.environ.get('LUCIDITY_TEMPLATE_PATH', '').split(os.pathsep)

for path in paths:
for base, directories, filenames in os.walk(path):
for filename in filenames:
_, extension = os.path.splitext(filename)
if extension != '.py':
continue

module_path = os.path.join(base, filename)
module_name = uuid.uuid4().hex
module = imp.load_source(module_name, module_path)
try:
registered = module.register()
except AttributeError:
pass
else:
if registered:
templates.extend(registered)

if not recursive:
del directories[:]

return templates


def parse(path, templates):
'''Parse *path* against *templates*.

*path* should be a string to parse.

*templates* should be a list of :py:class:`~lucidity.template.Template`
instances in the order that they should be tried.

Return ``(data, template)`` from first successful parse.

Raise :py:class:`~lucidity.error.ParseError` if *path* is not
parseable by any of the supplied *templates*.

'''
for template in templates:
try:
data = template.parse(path)
except ParseError:
continue
else:
return (data, template)

raise ParseError(
'Path {0!r} did not match any of the supplied template patterns.'
.format(path)
)


def format(data, templates): # @ReservedAssignment
'''Format *data* using *templates*.

*data* should be a dictionary of data to format into a path.

*templates* should be a list of :py:class:`~lucidity.template.Template`
instances in the order that they should be tried.

Return ``(path, template)`` from first successful format.

Raise :py:class:`~lucidity.error.FormatError` if *data* is not
formattable by any of the supplied *templates*.


'''
for template in templates:
try:
path = template.format(data)
except FormatError:
continue
else:
return (path, template)

raise FormatError(
'Data {0!r} was not formattable by any of the supplied templates.'
.format(data)
)


def get_template(name, templates):
'''Retrieve a template from *templates* by *name*.

Raise :py:exc:`~lucidity.error.NotFound` if no matching template with
*name* found in *templates*.

'''
for template in templates:
if template.name == name:
return template

raise NotFound(
'{0} template not found in specified templates.'.format(name)
)
from .error import (
ParseError,
FormatError,
NotFound
)

from .template import (
Template,
Resolver,
discover_templates,
parse,
format,
get_template
)

__all__ = [
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

This was added for PEP08, and the "unused variables" warning.

Copy link
Copy Markdown
Contributor

@martinpengellyphillips martinpengellyphillips May 3, 2017

Choose a reason for hiding this comment

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

Hmm. My understanding is that __all__ is only required when the package author wants to control behaviour of from package import *, which is considered bad practice anyway.

As I have no need to control this (the default behaviour is fine), I am not clear on the benefit of adding this here. Could you explain further perhaps?

Also, I don't get any "unused variables" warning - what are you using to lint / check the code?

"__version__",
"ParseError",
"FormatError",
"NotFound",
"Template",
"Resolver",
"discover_templates",
"parse",
"format",
"get_template",
]
Loading