Update and rename MantenerFIFO to MantenerFIFO.md
[vsorcdistro/.git] / ryu / .eggs / pbr-5.3.1-py2.7.egg / pbr / util.py
1 # Copyright (c) 2013 Hewlett-Packard Development Company, L.P.
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 #    http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
12 # implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
15 #
16 # Copyright (C) 2013 Association of Universities for Research in Astronomy
17 #                    (AURA)
18 #
19 # Redistribution and use in source and binary forms, with or without
20 # modification, are permitted provided that the following conditions are met:
21 #
22 #     1. Redistributions of source code must retain the above copyright
23 #        notice, this list of conditions and the following disclaimer.
24 #
25 #     2. Redistributions in binary form must reproduce the above
26 #        copyright notice, this list of conditions and the following
27 #        disclaimer in the documentation and/or other materials provided
28 #        with the distribution.
29 #
30 #     3. The name of AURA and its representatives may not be used to
31 #        endorse or promote products derived from this software without
32 #        specific prior written permission.
33 #
34 # THIS SOFTWARE IS PROVIDED BY AURA ``AS IS'' AND ANY EXPRESS OR IMPLIED
35 # WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
36 # MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
37 # DISCLAIMED. IN NO EVENT SHALL AURA BE LIABLE FOR ANY DIRECT, INDIRECT,
38 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
39 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
40 # OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
41 # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
42 # TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
43 # USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
44 # DAMAGE.
45
46 """The code in this module is mostly copy/pasted out of the distutils2 source
47 code, as recommended by Tarek Ziade.  As such, it may be subject to some change
48 as distutils2 development continues, and will have to be kept up to date.
49
50 I didn't want to use it directly from distutils2 itself, since I do not want it
51 to be an installation dependency for our packages yet--it is still too unstable
52 (the latest version on PyPI doesn't even install).
53 """
54
55 # These first two imports are not used, but are needed to get around an
56 # irritating Python bug that can crop up when using ./setup.py test.
57 # See: http://www.eby-sarna.com/pipermail/peak/2010-May/003355.html
58 try:
59     import multiprocessing  # noqa
60 except ImportError:
61     pass
62 import logging  # noqa
63
64 from collections import defaultdict
65 import io
66 import os
67 import re
68 import shlex
69 import sys
70 import traceback
71
72 import distutils.ccompiler
73 from distutils import errors
74 from distutils import log
75 import pkg_resources
76 from setuptools import dist as st_dist
77 from setuptools import extension
78
79 try:
80     import ConfigParser as configparser
81 except ImportError:
82     import configparser
83
84 from pbr import extra_files
85 import pbr.hooks
86
87 # A simplified RE for this; just checks that the line ends with version
88 # predicates in ()
89 _VERSION_SPEC_RE = re.compile(r'\s*(.*?)\s*\((.*)\)\s*$')
90
91
92 # Mappings from setup() keyword arguments to setup.cfg options;
93 # The values are (section, option) tuples, or simply (section,) tuples if
94 # the option has the same name as the setup() argument
95 D1_D2_SETUP_ARGS = {
96     "name": ("metadata",),
97     "version": ("metadata",),
98     "author": ("metadata",),
99     "author_email": ("metadata",),
100     "maintainer": ("metadata",),
101     "maintainer_email": ("metadata",),
102     "url": ("metadata", "home_page"),
103     "project_urls": ("metadata",),
104     "description": ("metadata", "summary"),
105     "keywords": ("metadata",),
106     "long_description": ("metadata", "description"),
107     "long_description_content_type": ("metadata", "description_content_type"),
108     "download_url": ("metadata",),
109     "classifiers": ("metadata", "classifier"),
110     "platforms": ("metadata", "platform"),  # **
111     "license": ("metadata",),
112     # Use setuptools install_requires, not
113     # broken distutils requires
114     "install_requires": ("metadata", "requires_dist"),
115     "setup_requires": ("metadata", "setup_requires_dist"),
116     "python_requires": ("metadata",),
117     "provides": ("metadata", "provides_dist"),  # **
118     "provides_extras": ("metadata",),
119     "obsoletes": ("metadata", "obsoletes_dist"),  # **
120     "package_dir": ("files", 'packages_root'),
121     "packages": ("files",),
122     "package_data": ("files",),
123     "namespace_packages": ("files",),
124     "data_files": ("files",),
125     "scripts": ("files",),
126     "py_modules": ("files", "modules"),   # **
127     "cmdclass": ("global", "commands"),
128     # Not supported in distutils2, but provided for
129     # backwards compatibility with setuptools
130     "use_2to3": ("backwards_compat", "use_2to3"),
131     "zip_safe": ("backwards_compat", "zip_safe"),
132     "tests_require": ("backwards_compat", "tests_require"),
133     "dependency_links": ("backwards_compat",),
134     "include_package_data": ("backwards_compat",),
135 }
136
137 # setup() arguments that can have multiple values in setup.cfg
138 MULTI_FIELDS = ("classifiers",
139                 "platforms",
140                 "install_requires",
141                 "provides",
142                 "obsoletes",
143                 "namespace_packages",
144                 "packages",
145                 "package_data",
146                 "data_files",
147                 "scripts",
148                 "py_modules",
149                 "dependency_links",
150                 "setup_requires",
151                 "tests_require",
152                 "keywords",
153                 "cmdclass",
154                 "provides_extras")
155
156 # setup() arguments that can have mapping values in setup.cfg
157 MAP_FIELDS = ("project_urls",)
158
159 # setup() arguments that contain boolean values
160 BOOL_FIELDS = ("use_2to3", "zip_safe", "include_package_data")
161
162 CSV_FIELDS = ()
163
164
165 def shlex_split(path):
166     if os.name == 'nt':
167         # shlex cannot handle paths that contain backslashes, treating those
168         # as escape characters.
169         path = path.replace("\\", "/")
170         return [x.replace("/", "\\") for x in shlex.split(path)]
171
172     return shlex.split(path)
173
174
175 def resolve_name(name):
176     """Resolve a name like ``module.object`` to an object and return it.
177
178     Raise ImportError if the module or name is not found.
179     """
180
181     parts = name.split('.')
182     cursor = len(parts) - 1
183     module_name = parts[:cursor]
184     attr_name = parts[-1]
185
186     while cursor > 0:
187         try:
188             ret = __import__('.'.join(module_name), fromlist=[attr_name])
189             break
190         except ImportError:
191             if cursor == 0:
192                 raise
193             cursor -= 1
194             module_name = parts[:cursor]
195             attr_name = parts[cursor]
196             ret = ''
197
198     for part in parts[cursor:]:
199         try:
200             ret = getattr(ret, part)
201         except AttributeError:
202             raise ImportError(name)
203
204     return ret
205
206
207 def cfg_to_args(path='setup.cfg', script_args=()):
208     """Distutils2 to distutils1 compatibility util.
209
210     This method uses an existing setup.cfg to generate a dictionary of
211     keywords that can be used by distutils.core.setup(kwargs**).
212
213     :param path:
214         The setup.cfg path.
215     :param script_args:
216         List of commands setup.py was called with.
217     :raises DistutilsFileError:
218         When the setup.cfg file is not found.
219     """
220
221     # The method source code really starts here.
222     if sys.version_info >= (3, 2):
223             parser = configparser.ConfigParser()
224     else:
225             parser = configparser.SafeConfigParser()
226     if not os.path.exists(path):
227         raise errors.DistutilsFileError("file '%s' does not exist" %
228                                         os.path.abspath(path))
229     try:
230         parser.read(path, encoding='utf-8')
231     except TypeError:
232         # Python 2 doesn't accept the encoding kwarg
233         parser.read(path)
234     config = {}
235     for section in parser.sections():
236         config[section] = dict()
237         for k, value in parser.items(section):
238             config[section][k.replace('-', '_')] = value
239
240     # Run setup_hooks, if configured
241     setup_hooks = has_get_option(config, 'global', 'setup_hooks')
242     package_dir = has_get_option(config, 'files', 'packages_root')
243
244     # Add the source package directory to sys.path in case it contains
245     # additional hooks, and to make sure it's on the path before any existing
246     # installations of the package
247     if package_dir:
248         package_dir = os.path.abspath(package_dir)
249         sys.path.insert(0, package_dir)
250
251     try:
252         if setup_hooks:
253             setup_hooks = [
254                 hook for hook in split_multiline(setup_hooks)
255                 if hook != 'pbr.hooks.setup_hook']
256             for hook in setup_hooks:
257                 hook_fn = resolve_name(hook)
258                 try:
259                     hook_fn(config)
260                 except SystemExit:
261                     log.error('setup hook %s terminated the installation')
262                 except Exception:
263                     e = sys.exc_info()[1]
264                     log.error('setup hook %s raised exception: %s\n' %
265                               (hook, e))
266                     log.error(traceback.format_exc())
267                     sys.exit(1)
268
269         # Run the pbr hook
270         pbr.hooks.setup_hook(config)
271
272         kwargs = setup_cfg_to_setup_kwargs(config, script_args)
273
274         # Set default config overrides
275         kwargs['include_package_data'] = True
276         kwargs['zip_safe'] = False
277
278         register_custom_compilers(config)
279
280         ext_modules = get_extension_modules(config)
281         if ext_modules:
282             kwargs['ext_modules'] = ext_modules
283
284         entry_points = get_entry_points(config)
285         if entry_points:
286             kwargs['entry_points'] = entry_points
287
288         # Handle the [files]/extra_files option
289         files_extra_files = has_get_option(config, 'files', 'extra_files')
290         if files_extra_files:
291             extra_files.set_extra_files(split_multiline(files_extra_files))
292
293     finally:
294         # Perform cleanup if any paths were added to sys.path
295         if package_dir:
296             sys.path.pop(0)
297
298     return kwargs
299
300
301 def setup_cfg_to_setup_kwargs(config, script_args=()):
302     """Convert config options to kwargs.
303
304     Processes the setup.cfg options and converts them to arguments accepted
305     by setuptools' setup() function.
306     """
307
308     kwargs = {}
309
310     # Temporarily holds install_requires and extra_requires while we
311     # parse env_markers.
312     all_requirements = {}
313
314     for arg in D1_D2_SETUP_ARGS:
315         if len(D1_D2_SETUP_ARGS[arg]) == 2:
316             # The distutils field name is different than distutils2's.
317             section, option = D1_D2_SETUP_ARGS[arg]
318
319         elif len(D1_D2_SETUP_ARGS[arg]) == 1:
320             # The distutils field name is the same thant distutils2's.
321             section = D1_D2_SETUP_ARGS[arg][0]
322             option = arg
323
324         in_cfg_value = has_get_option(config, section, option)
325         if not in_cfg_value:
326             # There is no such option in the setup.cfg
327             if arg == "long_description":
328                 in_cfg_value = has_get_option(config, section,
329                                               "description_file")
330                 if in_cfg_value:
331                     in_cfg_value = split_multiline(in_cfg_value)
332                     value = ''
333                     for filename in in_cfg_value:
334                         description_file = io.open(filename, encoding='utf-8')
335                         try:
336                             value += description_file.read().strip() + '\n\n'
337                         finally:
338                             description_file.close()
339                     in_cfg_value = value
340             else:
341                 continue
342
343         if arg in CSV_FIELDS:
344             in_cfg_value = split_csv(in_cfg_value)
345         if arg in MULTI_FIELDS:
346             in_cfg_value = split_multiline(in_cfg_value)
347         elif arg in MAP_FIELDS:
348             in_cfg_map = {}
349             for i in split_multiline(in_cfg_value):
350                 k, v = i.split('=', 1)
351                 in_cfg_map[k.strip()] = v.strip()
352             in_cfg_value = in_cfg_map
353         elif arg in BOOL_FIELDS:
354             # Provide some flexibility here...
355             if in_cfg_value.lower() in ('true', 't', '1', 'yes', 'y'):
356                 in_cfg_value = True
357             else:
358                 in_cfg_value = False
359
360         if in_cfg_value:
361             if arg in ('install_requires', 'tests_require'):
362                 # Replaces PEP345-style version specs with the sort expected by
363                 # setuptools
364                 in_cfg_value = [_VERSION_SPEC_RE.sub(r'\1\2', pred)
365                                 for pred in in_cfg_value]
366             if arg == 'install_requires':
367                 # Split install_requires into package,env_marker tuples
368                 # These will be re-assembled later
369                 install_requires = []
370                 requirement_pattern = (
371                     r'(?P<package>[^;]*);?(?P<env_marker>[^#]*?)(?:\s*#.*)?$')
372                 for requirement in in_cfg_value:
373                     m = re.match(requirement_pattern, requirement)
374                     requirement_package = m.group('package').strip()
375                     env_marker = m.group('env_marker').strip()
376                     install_requires.append((requirement_package, env_marker))
377                 all_requirements[''] = install_requires
378             elif arg == 'package_dir':
379                 in_cfg_value = {'': in_cfg_value}
380             elif arg in ('package_data', 'data_files'):
381                 data_files = {}
382                 firstline = True
383                 prev = None
384                 for line in in_cfg_value:
385                     if '=' in line:
386                         key, value = line.split('=', 1)
387                         key_unquoted = shlex_split(key.strip())[0]
388                         key, value = (key_unquoted, value.strip())
389                         if key in data_files:
390                             # Multiple duplicates of the same package name;
391                             # this is for backwards compatibility of the old
392                             # format prior to d2to1 0.2.6.
393                             prev = data_files[key]
394                             prev.extend(shlex_split(value))
395                         else:
396                             prev = data_files[key.strip()] = shlex_split(value)
397                     elif firstline:
398                         raise errors.DistutilsOptionError(
399                             'malformed package_data first line %r (misses '
400                             '"=")' % line)
401                     else:
402                         prev.extend(shlex_split(line.strip()))
403                     firstline = False
404                 if arg == 'data_files':
405                     # the data_files value is a pointlessly different structure
406                     # from the package_data value
407                     data_files = data_files.items()
408                 in_cfg_value = data_files
409             elif arg == 'cmdclass':
410                 cmdclass = {}
411                 dist = st_dist.Distribution()
412                 for cls_name in in_cfg_value:
413                     cls = resolve_name(cls_name)
414                     cmd = cls(dist)
415                     cmdclass[cmd.get_command_name()] = cls
416                 in_cfg_value = cmdclass
417
418         kwargs[arg] = in_cfg_value
419
420     # Transform requirements with embedded environment markers to
421     # setuptools' supported marker-per-requirement format.
422     #
423     # install_requires are treated as a special case of extras, before
424     # being put back in the expected place
425     #
426     # fred =
427     #     foo:marker
428     #     bar
429     # -> {'fred': ['bar'], 'fred:marker':['foo']}
430
431     if 'extras' in config:
432         requirement_pattern = (
433             r'(?P<package>[^:]*):?(?P<env_marker>[^#]*?)(?:\s*#.*)?$')
434         extras = config['extras']
435         # Add contents of test-requirements, if any, into an extra named
436         # 'test' if one does not already exist.
437         if 'test' not in extras:
438             from pbr import packaging
439             extras['test'] = "\n".join(packaging.parse_requirements(
440                 packaging.TEST_REQUIREMENTS_FILES)).replace(';', ':')
441
442         for extra in extras:
443             extra_requirements = []
444             requirements = split_multiline(extras[extra])
445             for requirement in requirements:
446                 m = re.match(requirement_pattern, requirement)
447                 extras_value = m.group('package').strip()
448                 env_marker = m.group('env_marker')
449                 extra_requirements.append((extras_value, env_marker))
450             all_requirements[extra] = extra_requirements
451
452     # Transform the full list of requirements into:
453     # - install_requires, for those that have no extra and no
454     #   env_marker
455     # - named extras, for those with an extra name (which may include
456     #   an env_marker)
457     # - and as a special case, install_requires with an env_marker are
458     #   treated as named extras where the name is the empty string
459
460     extras_require = {}
461     for req_group in all_requirements:
462         for requirement, env_marker in all_requirements[req_group]:
463             if env_marker:
464                 extras_key = '%s:(%s)' % (req_group, env_marker)
465                 # We do not want to poison wheel creation with locally
466                 # evaluated markers.  sdists always re-create the egg_info
467                 # and as such do not need guarded, and pip will never call
468                 # multiple setup.py commands at once.
469                 if 'bdist_wheel' not in script_args:
470                     try:
471                         if pkg_resources.evaluate_marker('(%s)' % env_marker):
472                             extras_key = req_group
473                     except SyntaxError:
474                         log.error(
475                             "Marker evaluation failed, see the following "
476                             "error.  For more information see: "
477                             "http://docs.openstack.org/"
478                             "pbr/latest/user/using.html#environment-markers"
479                         )
480                         raise
481             else:
482                 extras_key = req_group
483             extras_require.setdefault(extras_key, []).append(requirement)
484
485     kwargs['install_requires'] = extras_require.pop('', [])
486     kwargs['extras_require'] = extras_require
487
488     return kwargs
489
490
491 def register_custom_compilers(config):
492     """Handle custom compilers.
493
494     This has no real equivalent in distutils, where additional compilers could
495     only be added programmatically, so we have to hack it in somehow.
496     """
497
498     compilers = has_get_option(config, 'global', 'compilers')
499     if compilers:
500         compilers = split_multiline(compilers)
501         for compiler in compilers:
502             compiler = resolve_name(compiler)
503
504             # In distutils2 compilers these class attributes exist; for
505             # distutils1 we just have to make something up
506             if hasattr(compiler, 'name'):
507                 name = compiler.name
508             else:
509                 name = compiler.__name__
510             if hasattr(compiler, 'description'):
511                 desc = compiler.description
512             else:
513                 desc = 'custom compiler %s' % name
514
515             module_name = compiler.__module__
516             # Note; this *will* override built in compilers with the same name
517             # TODO(embray): Maybe display a warning about this?
518             cc = distutils.ccompiler.compiler_class
519             cc[name] = (module_name, compiler.__name__, desc)
520
521             # HACK!!!!  Distutils assumes all compiler modules are in the
522             # distutils package
523             sys.modules['distutils.' + module_name] = sys.modules[module_name]
524
525
526 def get_extension_modules(config):
527     """Handle extension modules"""
528
529     EXTENSION_FIELDS = ("sources",
530                         "include_dirs",
531                         "define_macros",
532                         "undef_macros",
533                         "library_dirs",
534                         "libraries",
535                         "runtime_library_dirs",
536                         "extra_objects",
537                         "extra_compile_args",
538                         "extra_link_args",
539                         "export_symbols",
540                         "swig_opts",
541                         "depends")
542
543     ext_modules = []
544     for section in config:
545         if ':' in section:
546             labels = section.split(':', 1)
547         else:
548             # Backwards compatibility for old syntax; don't use this though
549             labels = section.split('=', 1)
550         labels = [l.strip() for l in labels]
551         if (len(labels) == 2) and (labels[0] == 'extension'):
552             ext_args = {}
553             for field in EXTENSION_FIELDS:
554                 value = has_get_option(config, section, field)
555                 # All extension module options besides name can have multiple
556                 # values
557                 if not value:
558                     continue
559                 value = split_multiline(value)
560                 if field == 'define_macros':
561                     macros = []
562                     for macro in value:
563                         macro = macro.split('=', 1)
564                         if len(macro) == 1:
565                             macro = (macro[0].strip(), None)
566                         else:
567                             macro = (macro[0].strip(), macro[1].strip())
568                         macros.append(macro)
569                     value = macros
570                 ext_args[field] = value
571             if ext_args:
572                 if 'name' not in ext_args:
573                     ext_args['name'] = labels[1]
574                 ext_modules.append(extension.Extension(ext_args.pop('name'),
575                                                        **ext_args))
576     return ext_modules
577
578
579 def get_entry_points(config):
580     """Process the [entry_points] section of setup.cfg.
581
582     Processes setup.cfg to handle setuptools entry points. This is, of course,
583     not a standard feature of distutils2/packaging, but as there is not
584     currently a standard alternative in packaging, we provide support for them.
585     """
586
587     if 'entry_points' not in config:
588         return {}
589
590     return dict((option, split_multiline(value))
591                 for option, value in config['entry_points'].items())
592
593
594 def has_get_option(config, section, option):
595     if section in config and option in config[section]:
596         return config[section][option]
597     else:
598         return False
599
600
601 def split_multiline(value):
602     """Special behaviour when we have a multi line options"""
603
604     value = [element for element in
605              (line.strip() for line in value.split('\n'))
606              if element and not element.startswith('#')]
607     return value
608
609
610 def split_csv(value):
611     """Special behaviour when we have a comma separated options"""
612
613     value = [element for element in
614              (chunk.strip() for chunk in value.split(','))
615              if element]
616     return value
617
618
619 # The following classes are used to hack Distribution.command_options a bit
620 class DefaultGetDict(defaultdict):
621     """Like defaultdict, but get() also sets and returns the default value."""
622
623     def get(self, key, default=None):
624         if default is None:
625             default = self.default_factory()
626         return super(DefaultGetDict, self).setdefault(key, default)