Skip to content

Commit

Permalink
feat(pyratemp): successfully generating make deps
Browse files Browse the repository at this point in the history
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
  • Loading branch information
Byron committed Mar 1, 2015
1 parent 179c64c commit c0bfeab
Show file tree
Hide file tree
Showing 7 changed files with 269 additions and 36 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
target
.api.deps
Cargo.lock
*.sublime-workspace
*.xml
16 changes: 9 additions & 7 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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)


2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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].


Expand Down
20 changes: 20 additions & 0 deletions etc/api/shared.yaml
Original file line number Diff line number Diff line change
@@ -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
12 changes: 0 additions & 12 deletions etc/api/shared.yml

This file was deleted.

230 changes: 214 additions & 16 deletions etc/bin/pyratemp.py
Original file line number Diff line number Diff line change
Expand Up @@ -1162,20 +1162,25 @@ def render(self, parsetree, data):
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]
Expand Down Expand Up @@ -1279,6 +1284,201 @@ def __init__(self, string=None,filename=None,parsetree=None, encoding='utf-8', d
#=========================================



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
Expand Down Expand Up @@ -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:
Expand All @@ -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":
Expand All @@ -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

#-----------------------------------------
Expand Down Expand Up @@ -1466,4 +1665,3 @@ def load_data(datafiles):
sys.exit(30)

#-----------------------------------------

24 changes: 24 additions & 0 deletions src/pyra/deps.pyra
Original file line number Diff line number Diff line change
@@ -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)!$

0 comments on commit c0bfeab

Please sign in to comment.