"""One can load objects from different locations.
This module provides functionality to load objects from different locations
while preserving a simple interface to the cosumer.
"""
import json
import os
import sys
[docs]def identity(object_):
""":return: the argument
:param object_: the object to be returned"""
return object_
[docs]def true(_):
""":return: :obj:`True`
:param _: can be ignored"""
return True
[docs]class PathLoader(object):
"""Load paths and folders from the local file system.
The :paramref:`process <PathLoader.__init__.process>` is called with a
:class:`path <str>` as first argument: ``process(path)``.
"""
[docs] def __init__(self, process=identity, chooses_path=true):
"""Create a PathLoader object.
:param process: ``process(path)`` is called with the `path` to load.
The result of :paramref:`process` is returned to the caller. The
default value is :func:`identity`, so the paths are returned when
loaded.
:param chooses_path: ``chooses_path(path)`` is called before
:paramref:`process` and returns :obj:`True` or :obj:`False`
depending on whether a specific path should be loaded and passed to
:paramref:`process`.
"""
self._process = process
self._chooses_path = chooses_path
[docs] def folder(self, folder):
"""Load all files from a folder recursively.
Depending on :meth:`chooses_path` some paths may not be loaded.
Every loaded path is processed and returned part of the returned list.
:param str folder: the folder to load the files from
:rtype: list
:return: a list of the results of the processing steps of the loaded
files
"""
result = []
for root, _, files in os.walk(folder):
for file in files:
path = os.path.join(root, file)
if self._chooses_path(path):
result.append(self.path(path))
return result
[docs] def chooses_path(self, path):
""":return: whether the path should be loaded
:rtype: bool
:param str path: the path to the file to be tested
"""
return self._chooses_path(path)
[docs] def path(self, path):
"""load a :paramref:`path` and return the processed result
:param str path: the path to the file to be processed
:return: the result of processing step
"""
return self._process(path)
def _relative_to_absolute(self, module_location, folder):
""":return: the absolute path for the `folder` relative to
the module_location.
:rtype: str
"""
if os.path.isfile(module_location):
path = os.path.dirname(module_location)
elif os.path.isdir(module_location):
path = module_location
else:
module_folder = os.path.dirname(module_location)
if module_folder:
path = module_folder
else:
__import__(module_location)
module = sys.modules[module_location]
path = os.path.dirname(module.__file__)
absolute_path = os.path.join(path, folder)
return absolute_path
[docs] def relative_folder(self, module, folder):
"""Load a folder located relative to a module and return the processed
result.
:param str module: can be
- a path to a folder
- a path to a file
- a module name
:param str folder: the path of a folder relative to :paramref:`module`
:return: a list of the results of the processing
:rtype: list
Depending on :meth:`chooses_path` some paths may not be loaded.
Every loaded path is processed and returned part of the returned list.
You can use :meth:`choose_paths` to find out which paths are chosen to
load.
"""
folder = self._relative_to_absolute(module, folder)
return self.folder(folder)
[docs] def relative_file(self, module, file):
"""Load a file relative to a module.
:param str module: can be
- a path to a folder
- a path to a file
- a module name
:param str folder: the path of a folder relative to :paramref:`module`
:return: the result of the processing
"""
path = self._relative_to_absolute(module, file)
return self.path(path)
[docs] def choose_paths(self, paths):
""":return: the paths that are chosen by :meth:`chooses_path`
:rtype: list
"""
return [path for path in paths if self._chooses_path(path)]
[docs] def example(self, relative_path):
"""Load an example from the knitting pattern examples.
:param str relative_path: the path to load
:return: the result of the processing
You can use :meth:`knittingpattern.Loader.PathLoader.examples`
to find out the paths of all examples.
"""
example_path = os.path.join("examples", relative_path)
return self.relative_file(__file__, example_path)
[docs] def examples(self):
"""Load all examples form the examples folder of this packge.
:return: a list of processed examples
:rtype: list
Depending on :meth:`chooses_path` some paths may not be loaded.
Every loaded path is processed and returned part of the returned list.
"""
return self.relative_folder(__file__, "examples")
[docs]class ContentLoader(PathLoader):
"""Load contents of files and ressources.
The :paramref:`process <PathLoader.__init__.process>` is called with a
:class:`string <str>` as first argument: ``process(string)``.
"""
[docs] def string(self, string):
""":return: the processed result of a string
:param str string: the string to load the ocntent from
"""
return self._process(string)
[docs] def file(self, file):
""":return: the processed result of the content of a file-like object.
:param file: the file-like object to load the content from.
It should support the ``read`` method.
"""
string = file.read()
return self.string(string)
[docs] def path(self, path):
""":return: the processed result of a :paramref:`path's <path>` content.
:param str path: the path where to load the content from.
It should exist on the local file system.
"""
with open(path) as file:
return self.file(file)
[docs] def url(self, url, encoding="UTF-8"):
"""load and process the content behind a url
:return: the processed result of the :paramref:`url's <url>` content
:param str url: the url to retrieve the content from
:param str encoding: the encoding of the retrieved content.
The default encoding is UTF-8.
"""
import urllib.request
with urllib.request.urlopen(url) as file:
webpage_content = file.read()
webpage_content = webpage_content.decode(encoding)
return self.string(webpage_content)
[docs]class JSONLoader(ContentLoader):
"""Load an process JSON from various locations.
The :paramref:`process <PathLoader.__init__.process>` is called with an
:class:`object` as first argument: ``process(object)``.
"""
[docs] def object(self, object_):
"""Processes an already loaded object.
:return: the result of the processing step
:param object: the object to be loaded
"""
return self._process(object_)
[docs] def string(self, string):
"""Load an object from a string and return the processed JSON content
:return: the result of the processing step
:param str string: the string to load the JSON from
"""
object_ = json.loads(string)
return self.object(object_)
__all__ = ["JSONLoader", "ContentLoader", "PathLoader", "true", "identity"]