feat(pyratemp): successfully generating make deps

After minor modifications to pyratemp, it certainly does the job.

What it **does NOT** do:

* multiple outputs per template/command invocation
* NICE embedding of code (like GSL can)

It will do the job nonetheless, but mako might be worth a look
This commit is contained in:
Sebastian Thiel
2015-03-01 15:08:37 +01:00
parent 179c64c5e7
commit c0bfeabbc3
7 changed files with 269 additions and 36 deletions

1
.gitignore vendored
View File

@@ -1,4 +1,5 @@
target
.api.deps
Cargo.lock
*.sublime-workspace
*.xml

View File

@@ -5,27 +5,29 @@ include Makefile.helpers
PYTHON = python2.7
TPL = etc/bin/pyratemp.py
API_DEPS_TPL = src/pyra/deps.pyra
API_MAIN_TPL = src/pyra/main.pyra
API_DEPS = .api.deps
API_SHARED_INFO = ./etc/api/shared.yml
API_SHARED_INFO = ./etc/api/shared.yaml
API_JSON_FILES = $(shell find ./etc -type f -name '*-api.json')
help:
$(info Programs)
$(info ----> GSL: '$(GSL)')
$(info ----> templat engine: '$(TPL)')
$(info )
$(info Targets)
$(info help - print this help)
$(info api-deps - generate a file to tell make what API file dependencies will be)
$(info help-api - show all api targets to build individually)
json-to-xml: $(API_XML_FILES)
$(API_DEPS): $(API_SHARED_INFO)
$(TPL) -f $(API_SHARED_INFO) -d DEP_FILE=$@
$(API_DEPS): $(API_SHARED_INFO) $(API_DEPS_TPL)
$(TPL) -f $(API_SHARED_INFO) $(API_DEPS_TPL) > $@
api-deps: $(API_DEPS)
clean:
include $(API_DEPS)
clean: clean-api
-rm $(API_DEPS)
-rm $(API_XML_FILES)

View File

@@ -21,7 +21,7 @@ The license of everything not explicitly under a different license are licensed
What follows is a list of other material that is licensed differently.
* **./etc/bin/pyratemp.py** is licensed under MIT, as shown in [the header][pyratemp-header] of the file.
* **./etc/bin/pyratemp.py** is licensed under MIT-like, as shown in [the header][pyratemp-header] of the file.
* **./etc/api/\*\*/*.json** are licensed under a [MIT-like google license][google-lic].

20
etc/api/shared.yaml Normal file
View File

@@ -0,0 +1,20 @@
# Contains values shared among all API implementations
directories:
# directory under which all generated sources should reside
output: ./generated
# how to get from `output` back to common library
common: ../
# where are all the API meta files
api_base: ./etc/api
api:
list:
- name: youtube
version: v3
base_path: "etc/api"
cargo:
build_version: "0.0.1"
authors:
- Sebastian Thiel <byronimo@gmail>
keywords: [google, protocol]
# All APIs should live in the same repository
repository_url: https://github.com/Byron/youtube-rs

View File

@@ -1,12 +0,0 @@
# Contains values shared among all API implementations
apis:
base_path: "etc/api"
- name: youtube
version: v3
cargo:
build_version: "0.0.1"
authors:
- Sebastian Thiel <byronimo@gmail>
keywords: [google, protocol]
# All APIs should live in the same repository
repository_url: https://github.com/Byron/youtube-rs

View File

@@ -1162,20 +1162,25 @@ class Renderer(object):
output = []
do_else = False # use else/elif-branch?
def to_str(v):
if v is None:
return unicode()
return unicode(v)
if parsetree is None:
return ""
for elem in parsetree:
if "str" == elem[0]:
output.append(elem[1])
elif "sub" == elem[0]:
output.append(unicode(_eval(elem[1], data)))
output.append(to_str(_eval(elem[1], data)))
elif "esc" == elem[0]:
obj = _eval(elem[2], data)
#prevent double-escape
if isinstance(obj, _dontescape) or isinstance(obj, TemplateBase):
output.append(unicode(obj))
output.append(to_str(obj))
else:
output.append(self.escapefunc(unicode(obj), elem[1]))
output.append(self.escapefunc(to_str(obj), elem[1]))
elif "for" == elem[0]:
do_else = True
(names, iterable) = elem[1:3]
@@ -1279,6 +1284,201 @@ class Template(TemplateBase):
#=========================================
class DictObject(object):
"""An object which wraps a dictionary to allow object.key access.
If the source dictionary doesn't contain any sub-dictionaries, the input
dict will be referenced. Otherwise it will be copied.
An attribute error is raised if a value is not accessible.
Please note that you cannot access dict keys which are not valid attribute names.
"""
_default_dict = dict()
_unpackable_types = (dict, tuple, list)
def __init__(self, indict=_default_dict):
"""Initialize this instance from an input dictionary. If it contains other dictionaries, those will
trigger their parent dictionaries to be copied, as they will be used as DictObject themselves and
placed in the copy accordingly.
NOTE: other DictObjects are used by reference. Generally, this type tries to perform the least
amount of copying possible."""
if indict is self._default_dict:
return
# end handle default instantiation, which makes us empty
if isinstance(indict, DictObject):
self.__dict__ = indict.__dict__
return
# END handle special case, be a reference
dct = indict
for key, val in dct.items():
if isinstance(val, self._unpackable_types):
dct = None
break
# END for each key-value pair
if dct is None:
dct = dict(indict)
def unpack(val):
"""unpack helper"""
if isinstance(val, dict):
val = DictObject(val)
elif isinstance(val, (tuple, list)):
val = type(val)(unpack(item) for item in val)
return val
# END unpack
for key, val in dct.items():
dct[key] = unpack(val)
# END for each k,v pair
# END handle recursive copy
self.__dict__ = dct
def __str__(self):
return str(self.__dict__)
def __repr__(self):
return repr(self.__dict__)
def __getattr__(self, name):
return object.__getattribute__(self, name)
def __getitem__(self, name):
try:
return getattr(self, name)
except AttributeError:
raise KeyError(name)
# end convert exception
def __setitem__(self, name, value):
setattr(self, name, value)
def __contains__(self, name):
return name in self.__dict__
def __len__(self):
return len(self.__dict__)
def __iter__(self):
return iter(self.__dict__)
def __eq__(self, other):
"""Compares a possibly expensive comparison"""
if isinstance(other, DictObject):
# EXPENSIVE !
return self.to_dict() == other.to_dict()
elif isinstance(other, dict):
return self.to_dict() == other
# end handle type of other
return self is other
def update(self, other, **kwargs):
"""Similar to dict.update"""
items = other
if hasattr(other, 'keys'):
items = other.items()
for item_list in (items, kwargs.items()):
for k, v in item_list:
setattr(self, k, v)
# end for each item list
def to_dict(self, recursive=False):
"""@return ourselves as normal dict
@param recursive if True, a recursive copy will be returned if required."""
if recursive:
def obtain_needs_copy(value):
"""figure out if a copy is required"""
if isinstance(value, DictObject):
return True
if isinstance(value, (tuple, list, set)):
for item in value:
if obtain_needs_copy(item):
return True
# end check needs copy
# end for each item in value
# end if instance is iterable
return False
# end check needs copy
def unpack(val):
"""unpack val recursively and copy it gently"""
if isinstance(val, DictObject):
val = val.to_dict(recursive)
elif isinstance(val, (tuple, list, set)):
val = type(val)(unpack(item) for item in val)
# end handle type resolution
return val
# end unpack
needs_copy = False
for value in self.__dict__.values():
if obtain_needs_copy(value):
needs_copy = True
break
# end check value
# END for each value
if needs_copy:
new_dict = dict()
for key, val in self.__dict__.items():
new_dict[key] = unpack(val)
# END for each key, value pair
return new_dict
# else:
# just fall through and return ourselves as dictionary
# END handle recursion
return self.__dict__
def copy(self):
"""@return a (deep) copy of self"""
return type(self)(self.to_dict())
def clone(self):
"""@return a deep copy of this dict. This onyl means that the key-sets are independent. However, the
values are still shared, which matters in case of lists for instance"""
return type(self)(deepcopy(self.to_dict(recursive=True)))
def inversed_dict(self):
"""@return new dictionary which uses this dicts keys as values, and values as keys
@note duplicate values will result in just a single key, effectively drupping items.
Use this only if you have unique key-value pairs"""
return dict(list(zip(list(self.__dict__.values()), list(self.__dict__.keys()))))
def get(self, name, default=None):
"""as dict.get"""
return self.__dict__.get(name, default)
def keys(self):
"""as dict.keys"""
return list(self.__dict__.keys())
def values(self):
"""as dict.values"""
return list(self.__dict__.values())
def items(self):
"""as dict.items"""
return list(self.__dict__.items())
def iteritems(self):
"""as dict.iteritems"""
return iter(self.__dict__.items())
def pop(self, key, default=re):
"""as dict.pop"""
if default is re:
return self.__dict__.pop(key)
else:
return self.__dict__.pop(key, default)
# end assure semantics are kept
# end class DictObject
__version__ = "0.3.2"
__author__ = "Roland Koebler <rk at simple-is-better dot org>"
__license__ = """Copyright (c) 2007-2013 by Roland Koebler
@@ -1344,6 +1544,7 @@ def load_data(datafiles):
mydata = {}
for filename, n, namespace in datafiles:
data = None
if filename[-5:].lower() == ".json":
if not imported_json:
try:
@@ -1352,14 +1553,9 @@ def load_data(datafiles):
import json
imported_json = True
try:
myjson = json.load(open(filename, 'r'))
data = json.load(open(filename, 'r'))
if n != -1:
myjson = myjson[n]
if namespace is None:
mydata.update(myjson)
else:
mydata.update({namespace: myjson})
data = data[n]
except ValueError as err:
raise ValueError("Invalid JSON in file '%s'. (%s)" % (filename, str(err)))
elif filename[-5:].lower() == ".yaml":
@@ -1368,13 +1564,16 @@ def load_data(datafiles):
imported_yaml = True
if n == -1:
n = 0
myyaml = yaml.load_all(open(filename, 'r'))
if namespace is not None:
mydata.update({namespace: list(myyaml)[n]})
else:
mydata.update(list(myyaml)[n])
data = yaml.load_all(open(filename, 'r'))
data = list(data)[n]
else:
raise ValueError("Invalid data-file '%s', must be .json or .yaml" % filename)
assert data is not None
data = DictObject(data)
if namespace is None:
mydata.update(data)
else:
mydata.update({namespace: data})
return mydata
#-----------------------------------------
@@ -1466,4 +1665,3 @@ if __name__ == "__main__":
sys.exit(30)
#-----------------------------------------

24
src/pyra/deps.pyra Normal file
View File

@@ -0,0 +1,24 @@
$!setvar("api_info", "[]")!$#!
<!--(for a in api.list)-->
$!setvar("gen_root", "directories.output + '/' + a.name + '_' + a.version")!$#!
$!setvar("api_name", "a.name+a.version")!$#!
$!setvar("api_clean", "api_name+'-clean'")!$#!
$!gen_root!$: $!directories.api_base!$/$!a.name!$/$!a.version!$/$!a.name!$-api.json
$!api_name!$: $!gen_root!$
$!api_clean!$:
rm -Rf $!gen_root!$
$!api_info.append((api_name, api_clean, gen_root))!$#!
<!--(end)-->
.PHONY += $(.PHONY) $!' '.join(a[0] for a in api_info)!$ $!' '.join(a[1] for a in api_info)!$
help-api:
<!--(for a in api_info)-->
$(info $!a[0]!$ - build the $!a[0]!$ api)
$(info $!a[1]!$ - clean all generated files of the $!a[0]!$ api)
<!--(end)-->
clean-api: $!' '.join(a[1] for a in api_info)!$