diff options
-rw-r--r-- | .hgignore | 1 | ||||
-rw-r--r-- | __init__.py (renamed from py/__init__.py) | 16 | ||||
-rw-r--r-- | comments.py (renamed from py/comments.py) | 0 | ||||
-rw-r--r-- | compat.py (renamed from py/compat.py) | 0 | ||||
-rw-r--r-- | composer.py (renamed from py/composer.py) | 0 | ||||
-rw-r--r-- | configobjwalker.py (renamed from py/configobjwalker.py) | 0 | ||||
-rw-r--r-- | constructor.py (renamed from py/constructor.py) | 0 | ||||
-rw-r--r-- | cyaml.py (renamed from py/cyaml.py) | 0 | ||||
-rw-r--r-- | dumper.py (renamed from py/dumper.py) | 0 | ||||
-rw-r--r-- | emitter.py (renamed from py/emitter.py) | 0 | ||||
-rw-r--r-- | error.py (renamed from py/error.py) | 0 | ||||
-rw-r--r-- | events.py (renamed from py/events.py) | 0 | ||||
-rw-r--r-- | loader.py (renamed from py/loader.py) | 2 | ||||
-rw-r--r-- | main.py (renamed from py/main.py) | 0 | ||||
-rw-r--r-- | nodes.py (renamed from py/nodes.py) | 0 | ||||
-rw-r--r-- | parser_.py (renamed from py/parser.py) | 0 | ||||
-rw-r--r-- | py/yaml.py | 527 | ||||
-rw-r--r-- | reader.py (renamed from py/reader.py) | 0 | ||||
-rw-r--r-- | representer.py (renamed from py/representer.py) | 0 | ||||
-rw-r--r-- | resolver.py (renamed from py/resolver.py) | 0 | ||||
-rw-r--r-- | scalarstring.py (renamed from py/scalarstring.py) | 0 | ||||
-rw-r--r-- | scanner.py (renamed from py/scanner.py) | 0 | ||||
-rw-r--r-- | serializer.py (renamed from py/serializer.py) | 0 | ||||
-rw-r--r-- | setup.py | 455 | ||||
-rw-r--r-- | tokens.py (renamed from py/tokens.py) | 0 |
25 files changed, 324 insertions, 677 deletions
@@ -11,6 +11,7 @@ build *.egg-info .tox .cache +_yaml.so README.pdf ruamel convert diff --git a/py/__init__.py b/__init__.py index ac41035..0da3598 100644 --- a/py/__init__.py +++ b/__init__.py @@ -2,6 +2,15 @@ from __future__ import absolute_import +_package_data = dict( + full_package_name='ruamel.yaml', + version_info=(0, 10, 7), + author='Anthon van der Neut', + author_email='a.van.der.neut@ruamel.eu', + description="ruamel.yaml is a YAML parser/emitter that supports roundtrip preservation of comments, seq/map flow style, and map key order", # NOQA + entry_points=None, + install_requires=['ruamel.base'] +) def _convert_version(tup): """create a PEP 386 pseudo-format conformant string from tuple tup""" @@ -21,7 +30,8 @@ def _convert_version(tup): return ret_val -version_info = (0, 10, 6) +# < +version_info = _package_data['version_info'] __version__ = _convert_version(version_info) del _convert_version @@ -32,6 +42,8 @@ try: except ImportError: __with_libyaml__ = False +import sys + # body extracted to main.py from .main import * @@ -39,5 +51,5 @@ def main(): # No direct import of yaml in order not to pollute namespace. # If other utility 'bodies' exist in this directory a module level # import here, would get you all of its initialisations/imports as well - from ruamel.yaml.yaml import main as util_main + from ruamel.yaml import main as util_main util_main() diff --git a/py/comments.py b/comments.py index 0db9457..0db9457 100644 --- a/py/comments.py +++ b/comments.py diff --git a/py/composer.py b/composer.py index 13436c8..13436c8 100644 --- a/py/composer.py +++ b/composer.py diff --git a/py/configobjwalker.py b/configobjwalker.py index 576adcd..576adcd 100644 --- a/py/configobjwalker.py +++ b/configobjwalker.py diff --git a/py/constructor.py b/constructor.py index 5ce8974..5ce8974 100644 --- a/py/constructor.py +++ b/constructor.py diff --git a/py/emitter.py b/emitter.py index 2e5ebdc..2e5ebdc 100644 --- a/py/emitter.py +++ b/emitter.py @@ -4,7 +4,7 @@ __all__ = ['BaseLoader', 'SafeLoader', 'Loader', 'RoundTripLoader'] from .reader import * from .scanner import * -from .parser import * +from .parser_ import * from .composer import * from .constructor import * from .resolver import * diff --git a/py/parser.py b/parser_.py index 0afb596..0afb596 100644 --- a/py/parser.py +++ b/parser_.py diff --git a/py/yaml.py b/py/yaml.py deleted file mode 100644 index 6eda0ab..0000000 --- a/py/yaml.py +++ /dev/null @@ -1,527 +0,0 @@ -# coding: utf-8 - -""" -this is the source for the yaml utility -""" - -from __future__ import print_function -from __future__ import absolute_import - - -import sys -import os -import io -from textwrap import dedent - -from ruamel.std.argparse import ProgramBase, option, sub_parser, version, \ - CountAction, SmartFormatter -# from ruamel.appconfig import AppConfig -from . import __version__ - -import ruamel.yaml -from ruamel.yaml.compat import ordereddict, DBG_TOKEN, DBG_EVENT, DBG_NODE -from ruamel.yaml.configobjwalker import configobj_walker - - -def yaml_to_html2(code): - buf = io.StringIO() - buf.write(u'<HTML>\n') - buf.write(u'<HEAD>\n') - buf.write(u'</HEAD>\n') - buf.write(u'<BODY>\n') - buf.write(u'<TABLE>\n') - if isinstance(code, dict): - for k in code: - buf.write(u' <TR>\n') - for x in [k] + code[k]: - buf.write(u' <TD>{0}</TD>\n'.format(x)) - buf.write(u' </TR>\n') - buf.write(u'<TABLE>\n') - buf.write(u'</BODY>\n') - buf.write(u'</HTML>\n') - return buf.getvalue() - - -def yaml_to_html(code, level): - if level == 2: - return yaml_to_html2(code) - elif level == 3: - return yaml_to_html3(code) - raise NotImplementedError - - -class YAML: - def __init__(self, args, config): - self._args = args - self._config = config - - def from_ini(self): - try: - from configobj import ConfigObj - except ImportError: - print("to convert from .ini you need to install configobj:") - print(" pip install configobj:") - sys.exit(1) - errors = 0 - doc = [] - cfg = ConfigObj(open(self._args.file)) - if self._args.test: - print(ruamel.yaml.dump(cfg)) - return - for line in configobj_walker(cfg): - doc.append(line) - joined = '\n'.join(doc) - rto = self.round_trip_single(joined) - if self._args.basename: - out_fn = os.path.splitext(self._args.file)[0] + '.yaml' - if self._args.verbose > 0: - print('writing', out_fn) - with open(out_fn, 'w') as fp: - print(rto, end='', file=fp) # already has eol at eof - else: - print(rto, end='') # already has eol at eof - # print() - # if rto != joined: - # self.diff(joined, rto, "test.ini") - return 1 if errors else 0 - - def test(self): - self._args.event = self._args.node = True - dbg = 0 - if self._args.event: - dbg |= DBG_EVENT - if self._args.node: - dbg |= DBG_NODE - os.environ['YAMLDEBUG'] = str(dbg) - if False: - x = ruamel.yaml.comment.Comment() - print(sys.getsizeof(x)) - return - - def print_input(input): - print(input, end='') - print('-' * 15) - - def print_tokens(input): - print('Tokens (from scanner) ' + '#' * 50) - tokens = ruamel.yaml.scan(input, ruamel.yaml.RoundTripLoader) - for idx, token in enumerate(tokens): - # print(token.start_mark) - # print(token.end_mark) - print("{0:2} {1}".format(idx, token)) - - def rt_events(input): - dumper = ruamel.yaml.RoundTripDumper - events = ruamel.yaml.parse(input, ruamel.yaml.RoundTripLoader) - print(ruamel.yaml.emit(events, indent=False, Dumper=dumper)) - - def rt_nodes(input): - dumper = ruamel.yaml.RoundTripDumper - nodes = ruamel.yaml.compose(input, ruamel.yaml.RoundTripLoader) - print(ruamel.yaml.serialize(nodes, indent=False, Dumper=dumper)) - - def print_events(input): - print('Events (from parser) ' + '#' * 50) - events = ruamel.yaml.parse(input, ruamel.yaml.RoundTripLoader) - for idx, event in enumerate(events): - print("{0:2} {1}".format(idx, event)) - - def print_nodes(input): - print('Nodes (from composer) ' + '#' * 50) - x = ruamel.yaml.compose(input, ruamel.yaml.RoundTripLoader) - x.dump() # dump the node - - def scan_file(file_name): - inp = open(file_name).read() - print('---------\n', file_name) - print('---', repr(self.first_non_empty_line(inp))) - print('<<<', repr(self.last_non_empty_line(inp))) - - if False: - for x in self._args.file: - scan_file(x) - return - - if True: - import pickle - lines = 0 - for x in self._args.file: - print(x, end=' ') - if x.endswith('.yaml'): - data = ruamel.yaml.load(open(x)) - print(len(data), end=' ') - lines += len(data) - out_name = x.replace('.yaml', '.pickle') - with open(out_name, 'w') as fp: - pickle.dump(data, fp) - elif x.endswith('.pickle'): - with open(x) as fp: - data = pickle.load(fp) - print(len(data), end=' ') - lines += len(data) - print() - print('lines', lines) - return - - input = dedent(""" - application: web2py - version: 1 - runtime: python27 - api_version: 1 - threadsafe: false - - default_expiration: "24h" - - handlers: - - url: /(?P<a>.+?)/static/(?P<b>.+) - static_files: 'applications/\\1/static/\\2' - upload: applications/(.+?)/static/(.+) - secure: optional - """) - - input = dedent("""\ - a: - b: foo - c: bar - """) - - print_input(input) - print_tokens(input) - print_events(input) - # rt_events(input) - print_nodes(input) - # rt_nodes(input) - - data = ruamel.yaml.load(input, ruamel.yaml.RoundTripLoader) - print('data', data) - if False: - data['american'][0] = 'Fijenoord' - l = data['american'] - l = data - if True: - # print type(l), '\n', dir(l) - comment = getattr(l, '_yaml_comment', None) - print('comment_1', comment) - dumper = ruamel.yaml.RoundTripDumper - print('>>>>>>>>>>') - # print(ruamel.yaml.dump(data, default_flow_style=False, - # Dumper=dumper), '===========') - print("{0}=========".format(ruamel.yaml.dump( - data, - indent=4, - Dumper=dumper))) - comment = getattr(l, '_yaml_comment', None) - print('comment_2', comment) - - # test end - - def from_json(self): - # use roundtrip to preserve order - errors = 0 - docs = [] - for file_name in self._args.file: - if file_name == '-': - inp = sys.stdin.read() - else: - inp = open(file_name).read() - loader = ruamel.yaml.Loader # RoundTripLoader - docs.append(ruamel.yaml.load(inp, loader)) - #if self._args.literal: - # from ruamel.yaml.convert.literal import walk_tree - # for doc in docs: - # walk_tree(doc) - dumper = ruamel.yaml.RoundTripDumper - print(ruamel.yaml.dump_all( - docs, Dumper=dumper, - default_flow_style=self._args.flow)) - return 1 if errors else 0 - - def to_htmltable(self): - def vals(x): - if isinstance(x, list): - return x - if isinstance(x, (dict, ordereddict)): - return x.values() - return [] - - def seek_levels(x, count=0): - my_level = count - sub_level = 0 - for v in vals(x): - if v is None: - continue - sub_level = max(sub_level, seek_levels(v, my_level+1)) - return max(my_level, sub_level) - - inp = open(self._args.file).read() - loader = ruamel.yaml.RoundTripLoader - code = ruamel.yaml.load(inp, loader) - # assert isinstance(code, [ruamel.yaml.comments.CommentedMap]) - assert isinstance(code, (dict, list)) - levels = seek_levels(code) - if self._args.level: - print("levels:", levels) - return - print(yaml_to_html(code, levels)) - - def from_html(self): - from .convert.html import HTML2YAML - h2y = HTML2YAML(self._args) - with open(self._args.file) as fp: - print(h2y(fp.read())) - - def from_csv(self): - from .convert.from_csv import CSV2YAML - c2y = CSV2YAML(self._args) - c2y(self._args.file) - - def round_trip(self): - errors = 0 - warnings = 0 - for file_name in self._args.file: - inp = open(file_name).read() - e, w, stabilize, outp = self.round_trip_input(inp) - if w == 0: - if self._args.verbose > 0: - print(u"{0}: ok".format(file_name)) - continue - if self._args.save: - backup_file_name = file_name + '.orig' - if not os.path.exists(backup_file_name): - os.rename(file_name, backup_file_name) - with open(file_name, 'w') as ofp: - ofp.write(outp) - if not self._args.save or self._args.verbose > 0: - print("{0}:\n {1}".format(file_name, u', '.join(stabilize))) - self.diff(inp, outp, file_name) - errors += e - warnings += w - if errors > 0: - return 2 - if warnings > 0: - return 1 - return 0 - - def round_trip_input(self, inp): - errors = 0 - warnings = 0 - stabilize = [] - outp = self.round_trip_single(inp) - if inp == outp: - return errors, warnings, stabilize, outp - warnings += 1 - if inp.split() != outp.split(): - errors += 1 - stabilize.append(u"drops info on round trip") - else: - if self.round_trip_single(outp) == outp: - stabilize.append(u"stabilizes on second round trip") - else: - errors += 1 - ncoutp = self.round_trip_single(inp, drop_comment=True) - if self.round_trip_single(ncoutp, drop_comment=True) == ncoutp: - stabilize.append(u"ok without comments") - return errors, warnings, stabilize, outp - - def round_trip_single(self, inp, drop_comment=False): - explicit_start=self.first_non_empty_line(inp) == '---' - explicit_end=self.last_non_empty_line(inp) == '...' - indent = self._args.indent - loader = ruamel.yaml.SafeLoader if drop_comment else \ - ruamel.yaml.RoundTripLoader - code = ruamel.yaml.load(inp, loader) - dumper = ruamel.yaml.SafeDumper if drop_comment else \ - ruamel.yaml.RoundTripDumper - return ruamel.yaml.dump( - code, - Dumper=dumper, - indent=indent, - explicit_start=explicit_start, - explicit_end=explicit_end, - ) - - def first_non_empty_line(self, txt): - """return the first non-empty line of a block of text (stripped) - do not split or strip the complete txt - """ - pos = txt.find('\n') - prev_pos = 0 - while pos >= 0: - segment = txt[prev_pos:pos].strip() - if segment: - break - # print (pos, repr(segment)) - prev_pos = pos - pos = txt.find('\n', pos+1) - return segment - - def last_non_empty_line(self, txt): - """return the last non-empty line of a block of text (stripped) - do not split or strip the complete txt - """ - pos = txt.rfind('\n') - prev_pos = len(txt) - maxloop = 10 - while pos >= 0: - segment = txt[pos:prev_pos].strip() - if segment: - break - # print (pos, repr(segment)) - prev_pos = pos - pos = txt.rfind('\n', 0, pos-1) - maxloop -= 1 - if maxloop < 0: - break - return segment - - def diff(self, inp, outp, file_name): - import difflib - inl = inp.splitlines(True) # True for keepends - outl = outp.splitlines(True) - diff = difflib.unified_diff(inl, outl, file_name, 'round trip YAML') - # 2.6 difflib has trailing space on filename lines %-) - strip_trailing_space = sys.version_info < (2, 7) - for line in diff: - if strip_trailing_space and line[:4] in ['--- ', '+++ ']: - line = line.rstrip() + '\n' - sys.stdout.write(line) - - -def to_stdout(*args): - sys.stdout.write(' '.join(args)) - - -class YAML_Cmd(ProgramBase): - def __init__(self): - super(YAML_Cmd, self).__init__( - formatter_class=SmartFormatter, - aliases=True, - ) - self._config = None - - # you can put these on __init__, but subclassing YAML_Cmd - # will cause that to break - @option('--verbose', '-v', - help='increase verbosity level', action=CountAction, - const=1, nargs=0, default=0, global_option=True) - @option('--indent', type=int, global_option=True) - @version('version: ' + __version__) - def _pb_init(self): - # special name for which attribs are included in help - pass - - def run(self): - self._yaml = YAML(self._args, self._config) - if self._args.func: - return self._args.func() - - def parse_args(self): - # self._config = AppConfig( - # 'yaml', - # filename=AppConfig.check, - # parser=self._parser, # sets --config option - # warning=to_stdout, - # add_save=False, # add a --save-defaults (to config) option - # ) - # self._config._file_name can be handed to objects that need - # to get other information from the configuration directory - # self._config.set_defaults() - self._parse_args() - - @sub_parser( - aliases=['round-trip'], - help='test round trip on YAML data', - description='test round trip on YAML data', - ) - @option('--save', action='store_true', help="""save the rewritten data back - to the input file (if it doesn't exist a '.orig' backup will be made) - """) - @option('file', nargs='+') - def rt(self): - return self._yaml.round_trip() - - @sub_parser( - aliases=['from-json'], - help='convert json to block YAML', - description='convert json to block YAML', - ) - @option('--flow', action='store_true', - help='use flow instead of block style') - #@option('--literal', action='store_true', - # help='convert scalars with newlines to literal block style') - @option('file', nargs='+') - def json(self): - return self._yaml.from_json() - - @sub_parser( - aliases=['from-ini'], - help='convert .ini/config to block YAML', - description='convert .ini/config to block YAML', - ) - @option('--basename', '-b', action='store_true', - help='re-use basename of file for .yaml file, instead of writing' - ' to stdout') - @option('--test', action='store_true') - @option('file') - def ini(self): - return self._yaml.from_ini() - - @sub_parser( - #aliases=['to-html'], - help='convert YAML to html tables', - description="""convert YAML to html tables. If hierarchy is two deep ( - sequence/mapping over sequence/mapping) this is mapped to one table - If the hierarchy is three deep, a list of 2 deep tables is assumed, but - any non-list/mapp second level items are considered text. - Row level keys are inserted in first column (unless --no-row-key), - item level keys are used as classes for the TD. - """, - ) - @option("--level", action='store_true', help="print # levels and exit") - @option('file') - def htmltable(self): - return self._yaml.to_htmltable() - - @sub_parser('from-html', - help='convert HTML to YAML', - description="""convert HTML to YAML. Tags become keys with as - value a list. The first item in the list is a key value pair with - key ".attribute" if attributes are available followed by tag and string - segment items. Lists with one item are by default flattened. - """, - ) - @option("--no-body", action='store_true', - help="drop top level html and body from HTML code segments") - @option("--strip", action='store_true', - help="strip whitespace surrounding strings") - @option('file') - def from_html(self): - return self._yaml.from_html() - - @sub_parser('from-csv', - aliases=['csv'], - help='convert CSV to YAML', - description="""convert CSV to YAML. - By default generates a list of rows, with the items in a 2nd level - list. - """, - ) - @option('--delimiter') - #@option('--quotechar') - @option('file') - def from_csv(self): - return self._yaml.from_csv() - - if 'test' in sys.argv: - @sub_parser( - description='internal test function', - ) - @option('file', nargs='*') - def test(self): - return self._yaml.test() - - -def main(): - n = YAML_Cmd() - n.parse_args() - sys.exit(n.run()) diff --git a/py/representer.py b/representer.py index 63c0d0d..63c0d0d 100644 --- a/py/representer.py +++ b/representer.py diff --git a/py/resolver.py b/resolver.py index d385b0e..d385b0e 100644 --- a/py/resolver.py +++ b/resolver.py diff --git a/py/scalarstring.py b/scalarstring.py index a628396..a628396 100644 --- a/py/scalarstring.py +++ b/scalarstring.py diff --git a/py/scanner.py b/scanner.py index 24a13a8..24a13a8 100644 --- a/py/scanner.py +++ b/scanner.py diff --git a/py/serializer.py b/serializer.py index 0bdc558..0bdc558 100644 --- a/py/serializer.py +++ b/serializer.py @@ -1,34 +1,68 @@ -#! /usr/bin/env python +# # header # coding: utf-8 from __future__ import print_function -import sys -import os -import platform -from textwrap import dedent +if __name__ != '__main__': + raise NotImplementedError('should never include setup.py') + +# # definitions -name_space = 'ruamel' -package_name = 'yaml' -full_package_name = name_space + '.' + package_name +full_package_name = None + + +def _package_data(fn): + data = {} + with open(fn) as fp: + parsing = False + for line in fp.readlines(): + if line.startswith('_package_data'): + parsing = True + continue + if not parsing: + continue + if line.startswith(')'): + break + if '# NOQA' in line: + line = line.split('# NOQA', 1)[0].rstrip() + k, v = [x.strip() for x in line.split('=', 1)] + if v[-1] == ',': + v = v[:-1] + if v[0] in '\'"' and v[0] == v[-1]: + data[k] = v[1:-1] + elif v == 'None': + data[k] = None + elif v == 'True': + data[k] = True + elif v == 'False': + data[k] = False + elif v[0] == '(' and v[-1] == ')': + data[k] = tuple([x.strip()[1:-1] if x[0] in '\'"' else int(x) + for x in v[1:-1].split(', ')]) + elif v[0] == '[' and v[-1] == ']': + data[k] = [x.strip()[1:-1] if x[0] in '\'"' else int(x) + for x in v[1:-1].split(', ')] + else: + print('Unknown: >>>>> {0!r} {1!r}'.format(k, v)) + return data + +pkg_data = _package_data('__init__.py') exclude_files = [ 'setup.py', ] +# # imports +import os +import sys -def get_version(): - v_i = 'version_info = ' - for line in open('py/__init__.py'): - if not line.startswith(v_i): - continue - s_e = line[len(v_i):].strip()[1:-1].split(', ') - els = [x.strip()[1:-1] if x[0] in '\'"' else int(x) for x in s_e] - return els +from setuptools import setup +from setuptools.command import install_lib +# # helper def _check_convert_version(tup): - """create a PEP 386 pseudo-format conformant string from tuple tup""" + """Create a PEP 386 pseudo-format conformant string from tuple tup.""" ret_val = str(tup[0]) # first is always digit next_sep = "." # separator for next extension, can be "" or "." nr_digits = 0 # nr of adjacent digits in rest, to verify @@ -58,29 +92,13 @@ def _check_convert_version(tup): return ret_val -version_info = get_version() +version_info = pkg_data['version_info'] version_str = _check_convert_version(version_info) -if __name__ == '__main__': - # put here so setup.py can be imported more easily - from setuptools import setup, find_packages, Extension, Distribution - from setuptools.command import install_lib - - # windows things this is pure, that way you would get pure python - # whl files that take precedence on Linux over source with compilable C - if '--universal' in sys.argv: - Distribution.is_pure = lambda *args: True - Distribution.is_pure = lambda *args: False - - class MyInstallLib(install_lib.install_lib): - "create __init__.py on the fly" - def run(self): - install_lib.install_lib.run(self) - def install(self): - fpp = full_package_name.split('.') # full package path + fpp = pkg_data['full_package_name'].split('.') # full package path full_exclude_files = [os.path.join(*(fpp + [x])) for x in exclude_files] alt_files = [] @@ -95,127 +113,270 @@ class MyInstallLib(install_lib.install_lib): return alt_files -def check_extensions(): - """check if the C module can be build by trying to compile a small - program against the libyaml development library""" - if sys.platform == "win32": - return None - import tempfile - import shutil - - import distutils.sysconfig - import distutils.ccompiler - from distutils.errors import CompileError, LinkError - - libraries = ['yaml'] - - # write a temporary .c file to compile - c_code = dedent(""" - #include <yaml.h> - - int main(int argc, char* argv[]) - { - yaml_parser_t parser; - parser = parser; /* prevent warning */ - return 0; - } - """) - tmp_dir = tempfile.mkdtemp(prefix='tmp_ruamel_yaml_') - ret_val = None - try: - bin_file_name = os.path.join(tmp_dir, 'test_yaml') - file_name = bin_file_name + '.c' - with open(file_name, 'w') as fp: - fp.write(c_code) +class NameSpacePackager(object): + def __init__(self, pkg_data): + assert isinstance(pkg_data, dict) + self._pkg_data = pkg_data + self.full_package_name = self._pkg_data['full_package_name'] + self._split = None + self.depth = self.full_package_name.count('.') + self.command = None + if sys.argv[0] == 'setup.py' and sys.argv[1] == 'install' and \ + '--single-version-externally-managed' not in sys.argv: + print('error: have to install with "pip install ."') + sys.exit(1) + for x in sys.argv: + if x[0] == '-' or x == 'setup.py': + continue + self.command = x + break - # and try to compile it - compiler = distutils.ccompiler.new_compiler() - assert isinstance(compiler, distutils.ccompiler.CCompiler) - distutils.sysconfig.customize_compiler(compiler) + @property + def split(self): + """split the full package name in list of compontents""" + if self._split is None: + fpn = self.full_package_name.split('.') + self._split = [] + while fpn: + self._split.insert(0, '.'.join(fpn)) + fpn = fpn[:-1] + for d in os.listdir('.'): + if not os.path.isdir(d) or d == self._split[0] or d[0] == '_': + continue + x = os.path.join(d, 'setup.py') # prevent sub-packages in namespace from being included + if os.path.exists(x): + if not os.path.exists(os.path.join(d, 'tox.ini')): + print('\n>>>>> found "{0}" without tox.ini <<<<<\n' + ''.format(x)) + continue + x = os.path.join(d, '__init__.py') + if os.path.exists(x): + self._split.append(self.full_package_name + '.' + d) + return self._split - try: - compiler.link_executable( - compiler.compile([file_name]), - bin_file_name, - libraries=libraries, + @property + def namespace_packages(self): + return self.split[:self.depth] + + @property + def package_dir(self): + return { + # don't specify empty dir, clashes with package_data spec + self.full_package_name: '.', + self.split[0]: self.split[0], + } + + def create_dirs(self): + """create the directories necessary for namespace packaging""" + if not os.path.exists(self.split[0]): + for d in self.split[:self.depth]: + d = os.path.join(*d.split('.')) + os.mkdir(d) + with open(os.path.join(d, '__init__.py'), 'w') as fp: + fp.write('import pkg_resources\n' + 'pkg_resources.declare_namespace(__name__)\n') + os.symlink( + # a.b gives a/b -> .. + # a.b.c gives a/b/c -> ../.. + os.path.join(*['..'] * self.depth), + os.path.join(*self.split[self.depth].split('.')) ) - except CompileError: - print('libyaml compile error') - except LinkError: - print('libyaml link error') - else: - ret_val = [ - Extension( - '_yaml', - sources=['ext/_yaml.c'], - libraries=libraries, - ), - ] - except: - pass - finally: - shutil.rmtree(tmp_dir) - return ret_val + def check(self): + try: + from pip.exceptions import InstallationError + except ImportError: + return + # arg is either develop (pip install -e) or install + if self.command not in ['install', 'develop']: + return -def main(): - install_requires = [ - "ruamel.base>=1.0.0", - "ruamel.std.argparse", - ] - # use fast ordereddict for !!omap - if sys.version_info[0] == 2 and \ - platform.python_implementation() == "CPython": - install_requires.extend(['ruamel.ordereddict']) - # if sys.version_info < (3, 4): - # install_requires.append("") - packages = [full_package_name] + [ - (full_package_name + '.' + x) - for x in find_packages('py', exclude=['test'])] - setup( - name=full_package_name, - version=version_str, - description=full_package_name + " is a YAML parser/emitter that " - "supports roundtrip preservation of comments, seq/map flow style, and " - "map key order", - install_requires=install_requires, - long_description=open('README.rst').read(), - url='https://bitbucket.org/ruamel/' + package_name, - author='Anthon van der Neut', - author_email='a.van.der.neut@ruamel.eu', - license="MIT license", - package_dir={full_package_name: 'py'}, - namespace_packages=[name_space], - packages=packages, - ext_modules=check_extensions(), - #entry_points=mk_entry_points(full_package_name), - cmdclass={'install_lib': MyInstallLib}, - classifiers=[ - 'Development Status :: 4 - Beta', + # if hgi and hgi.base are both in namespace_packages matching + # against the top (hgi.) it suffices to find minus-e and non-minus-e + # installed packages. As we don't know the order in namespace_packages + # do some magic + prefix = self.split[0] + prefixes = set([prefix, prefix.replace('_', '-')]) + for p in sys.path: + if not p: + continue # directory with setup.py + if os.path.exists(os.path.join(p, 'setup.py')): + continue # some linked in stuff might not be hgi based + if not os.path.isdir(p): + continue + if p.startswith('/tmp/'): + continue + for fn in os.listdir(p): + for pre in prefixes: + if fn.startswith(pre): + break + else: + continue + full_name = os.path.join(p, fn) + # not in prefixes the toplevel is never changed from _ to - + if fn == prefix and os.path.isdir(full_name): + # directory -> other, non-minus-e, install + if self.command == 'develop': + raise InstallationError( + 'Cannot mix develop (pip install -e),\nwith ' + 'non-develop installs for package name {0}'.format( + fn)) + elif fn == prefix: + raise InstallationError( + 'non directory package {0} in {1}'.format( + fn, p)) + for pre in [x + '.' for x in prefixes]: + if fn.startswith(pre): + break + else: + continue # hgiabc instead of hgi. + if fn.endswith('-link') and self.command == 'install': + raise InstallationError( + 'Cannot mix non-develop with develop\n(pip install -e)' + ' installs for package name {0}'.format(fn)) + + def entry_points(self, script_name=None, package_name=None): + ep = self._pkg_data.get('entry_points', True) + if ep is None: + return None + if ep is not True: + return {'console_scripts': [ep]} + if package_name is None: + package_name = self.full_package_name + if not script_name: + script_name = package_name.split('.')[-1] + return {'console_scripts': [ + '{0} = {1}:main'.format(script_name, package_name), + ]} + + @property + def url(self): + return 'https://bitbucket.org/{0}/{1}'.format( + *self.full_package_name.split('.', 1)) + + @property + def author(self): + return self._pkg_data['author'] + + @property + def author_email(self): + return self._pkg_data['author_email'] + + @property + def license(self): + lic = self._pkg_data.get('license') + if lic is None: + # lic_fn = os.path.join(os.path.dirname(__file__), 'LICENSE') + # assert os.path.exists(lic_fn) + return "MIT license" + return license + + @property + def description(self): + return self._pkg_data['description'] + + @property + def status(self): + # αβ + status = self._pkg_data.get('status', 'β') + if status == 'α': + return (3, 'Alpha') + elif status == 'β': + return (4, 'Beta') + elif 'stable' in status.lower(): + return (5, 'Production/Stable') + raise NotImplementedError + + @property + def classifiers(self): + return [ + 'Development Status :: {0} - {1}'.format(*self.status), 'Intended Audience :: Developers', - 'License :: OSI Approved :: MIT License', + 'License :: ' + ('Other/Proprietary License' + if self._pkg_data.get('license') else + 'OSI Approved :: MIT License'), 'Operating System :: OS Independent', 'Programming Language :: Python', - "Programming Language :: Python :: 2.6", - "Programming Language :: Python :: 2.7", - "Programming Language :: Python :: 3.3", - "Programming Language :: Python :: 3.4", - "Programming Language :: Python :: Implementation :: CPython", - "Programming Language :: Python :: Implementation :: PyPy", - "Topic :: Software Development :: Libraries :: Python Modules", - "Topic :: Text Processing :: Markup", ] - ) + @property + def install_requires(self): + return pkg_data.get('install_requires', []) + + @property + def data_files(self): + df = self._pkg_data.get('data_files', []) + if self._pkg_data.get('license') is None: + df.append('LICENSE') + if not df: + return None + return [('.', df), ] + + @property + def package_data(self): + df = self._pkg_data.get('data_files', []) + if self._pkg_data.get('license') is None: + # include the file + df.append('LICENSE') + # but don't install it + exclude_files.append('LICENSE') + if not df: + return {} + return {self.full_package_name: df} + + def wheel(self, kw, setup): + """temporary add setup.cfg if creating a wheel to include LICENSE file + https://bitbucket.org/pypa/wheel/issues/47 + """ + if 'bdist_wheel' not in sys.argv or not os.path.exists('LICENSE'): + return + file_name = 'setup.cfg' + if os.path.exists(file_name): # add it if not in there? + return + with open(file_name, 'w') as fp: + fp.write('[metadata]\nlicense-file = LICENSE\n') + if self._pkg_data.get('universal'): + fp.write('[bdist_wheel]\nuniversal = 1\n') + try: + setup(**kw) + except: + raise + finally: + os.remove(file_name) + return True + + +# # call setup +def main(): + nsp = NameSpacePackager(pkg_data) + nsp.check() + nsp.create_dirs() + kw = dict( + name=nsp.full_package_name, + namespace_packages=nsp.namespace_packages, + version=version_str, + packages=nsp.split, + url=nsp.url, + author=nsp.author, + author_email=nsp.author_email, + cmdclass={'install_lib': MyInstallLib}, + package_dir=nsp.package_dir, + entry_points=nsp.entry_points(), + description=nsp.description, + install_requires=nsp.install_requires, + license=nsp.license, + classifiers=nsp.classifiers, + package_data=nsp.package_data, + ) + if '--version' not in sys.argv: + for k in sorted(kw): + v = kw[k] + print(k, '->', v) + with open('README.rst') as fp: + kw['long_description'] = fp.read() + if nsp.wheel(kw, setup): + return + setup(**kw) -def mk_entry_points(full_package_name): - script_name = full_package_name.rsplit('.', 1)[-1] - return {'console_scripts': [ - '{0} = {1}:main'.format(script_name, full_package_name), - ]} -if __name__ == '__main__': - if 'yamlctest' in sys.argv: - print(check_extensions()) - sys.exit(1) - main() +main() |