| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167 |
- import web
- import re
- import os.path
- from cache import Cache, to_dir, assert_file_exists
- ALIAS_PREFIX = '.'
- def separate_imports(content):
- """
- Separate the import section from the rest of the content of a file. Returns
- a tuple with a list of imports, and the remaning part of the string.
- Imports are located in the beginning of a file, and can take the following
- forms:
- # No imports
- content
- # Single file import
- import foo
- content
- # Multiple files import
- import foo, bar
- content
- # Multiline import
- import foo, bar, \
- baz
- content
- """
- content = content.lstrip()
- imports = []
- filename = '[a-zA-Z0-9\._-]+'
- comma = re.compile(' *, *(?:\\\\\r?\n *)?')
- file_list = '%s(?:%s%s?)*' % (filename, comma.pattern, filename)
- m = re.match('^import (%s)(?:(?:\r?\n)+(.*))?$' % file_list, content, re.M)
- if m:
- imports = comma.split(m.group(1))
- content = m.group(2)
- if not content:
- content = ''
- return imports, content
- class Importer(Cache):
- def __init__(self, files=[], extension=None, **kwargs):
- super(Importer, self).__init__(**kwargs)
- self.aliases = {}
- if extension:
- self.extension = '.' + extension
- else:
- self.extension = ''
- for f in files:
- self.add(f)
- def add(self, path):
- path = self.create_full_paths([path])[0]
- Cache.add(self, path, absolute=True)
- def set_alias(self, alias, path):
- """
- Add an alias for the path to a file, relative to the importer's root
- directory. Aliases are used to shorten the names of imports. E.g. using
- "@jquery" in an import could point to "static/js/jquery-min-1.7.1.js".
- """
- path = self.root + path + self.extension
- assert_file_exists(path)
- self.aliases[alias] = path
- web.debug('Added alias "%s" for path "%s".' % (alias, path))
- def create_full_paths(self, files, relative_file=None):
- """
- Create full paths out of a file list:
- 1. Replace any aliases.
- 2. Look for direct siblings of the loaded file, if any (relative_file).
- 3. Look in the root folder of the Import object.
- """
- replaced = []
- alias_len = len(ALIAS_PREFIX)
- if relative_file:
- relative_dir = to_dir(relative_file)
- for i, path in enumerate(files):
- if path[:alias_len] == ALIAS_PREFIX:
- # Alias syntax is used, assert that the alias exists
- alias = path[alias_len:]
- if alias not in self.aliases:
- raise ValueError('Alias "%s" has not been set.' % alias)
- # Alias exists, translate is to a full path
- replaced.append(self.aliases[alias])
- else:
- if relative_file:
- relative_path = relative_dir + path + self.extension
- if os.path.exists(relative_path):
- # Relative import
- replaced.append(relative_path)
- continue
- # Import from root
- path = self.root + path + self.extension
- assert_file_exists(path)
- replaced.append(path)
- return replaced
- def concatenate(self, files, relative_path=False):
- #if relative_path:
- # files = self.create_full_paths(files, relative_path)
- map(assert_file_exists, files)
- self.loaded = []
- self.import_map = {}
- concat = ''
- for path in files:
- f = open(path, 'r')
- raw = f.read()
- f.close()
- # Parse imports from file content
- imports, content = separate_imports(raw)
- self.import_map.update({path: imports})
- # Only add imported files that have not been loaded yet
- new_imports = filter(lambda i: i not in self.loaded, imports)
- # Prepend imports that have not been loaded yet before the file's
- # content
- if new_imports:
- new_imports = self.create_full_paths(new_imports, path)
- # Check if there is a recursive import
- for f in new_imports:
- if f == path:
- raise ImportError('Recursive import in file "%s".' % f)
- if f in self.import_map and path in self.import_map[f]:
- raise ImportError('Recursive import in of "%s" in ' \
- + 'file "%s".' % (f, path))
- concat += self.concatenate(new_imports)
- #concat += self.concatenate(new_imports, path)
- concat += content
- self.loaded += path
- return concat
- def content(self):
- return self.concatenate(self.files)
|