Vendor ordereddict for python2.6 only (#6931)

* Vendor ordereddict for python2.6 only

This commit substitutes the custom module 'ordereddict_backport' with
the more known 'ordereddict' and vendors it only for python 2.6. Other
supported versions of python will use 'collections.OrderedDict'.

* Use absolute imports also for python 2.6

See PEP-328 for more information on the subject

* Added provenance of vendored ordereddict
This commit is contained in:
Massimiliano Culpo 2018-01-16 07:00:39 +01:00 committed by GitHub
parent 498c522a31
commit 1fb38c9e04
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 185 additions and 281 deletions

View file

@ -43,6 +43,10 @@ sys.path.insert(0, spack_lib_path)
# Add external libs # Add external libs
spack_external_libs = os.path.join(spack_lib_path, "external") spack_external_libs = os.path.join(spack_lib_path, "external")
if sys.version_info[:2] == (2, 6):
sys.path.insert(0, os.path.join(spack_external_libs, 'py26'))
sys.path.insert(0, spack_external_libs) sys.path.insert(0, spack_external_libs)
# Handle vendoring of YAML specially, as it has two versions. # Handle vendoring of YAML specially, as it has two versions.

View file

@ -86,9 +86,10 @@
orderddict orderddict
---------- ----------
* Homepage: http://code.activestate.com/recipes/576693-ordered-dictionary-for-py24/ * Homepage: https://pypi.org/project/ordereddict/
* Usage: We include our own version to be Python 2.6 compatible. * Usage: A drop-in substitute for Py2.7's new collections.OrderedDict
* Version: Unversioned that works in Python 2.4-2.6.
* Version: 1.1
py py
-- --

View file

@ -1,268 +0,0 @@
#
# Backport of OrderedDict() class that runs on Python 2.4, 2.5, 2.6, 2.7 and pypy.
# Passes Python2.7's test suite and incorporates all the latest updates.
#
# From http://code.activestate.com/recipes/576693-ordered-dictionary-for-py24/
# This file is in the public domain, and has no particular license.
#
try:
from thread import get_ident as _get_ident
except ImportError:
try:
from dummy_thread import get_ident as _get_ident
except ImportError:
try:
from _dummy_thread import get_ident as _get_ident
except ImportError:
from threading import get_ident as _get_ident # nopyqver
try:
from _abcoll import KeysView, ValuesView, ItemsView
except ImportError:
pass
class OrderedDict(dict):
'Dictionary that remembers insertion order'
# An inherited dict maps keys to values.
# The inherited dict provides __getitem__, __len__, __contains__, and get.
# The remaining methods are order-aware.
# Big-O running times for all methods are the same as for regular dictionaries.
# The internal self.__map dictionary maps keys to links in a doubly linked list.
# The circular doubly linked list starts and ends with a sentinel element.
# The sentinel element never gets deleted (this simplifies the algorithm).
# Each link is stored as a list of length three: [PREV, NEXT, KEY].
def __init__(self, *args, **kwds):
'''Initialize an ordered dictionary. Signature is the same as for
regular dictionaries, but keyword arguments are not recommended
because their insertion order is arbitrary.
'''
if len(args) > 1:
raise TypeError('expected at most 1 arguments, got %d' % len(args))
try:
self.__root
except AttributeError:
self.__root = root = [] # sentinel node
root[:] = [root, root, None]
self.__map = {}
self.__update(*args, **kwds)
def __setitem__(self, key, value, dict_setitem=dict.__setitem__):
'od.__setitem__(i, y) <==> od[i]=y'
# Setting a new item creates a new link which goes at the end of the linked
# list, and the inherited dictionary is updated with the new key/value pair.
if key not in self:
root = self.__root
last = root[0]
last[1] = root[0] = self.__map[key] = [last, root, key]
dict_setitem(self, key, value)
def __delitem__(self, key, dict_delitem=dict.__delitem__):
'od.__delitem__(y) <==> del od[y]'
# Deleting an existing item uses self.__map to find the link which is
# then removed by updating the links in the predecessor and successor nodes.
dict_delitem(self, key)
link_prev, link_next, key = self.__map.pop(key)
link_prev[1] = link_next
link_next[0] = link_prev
def __iter__(self):
'od.__iter__() <==> iter(od)'
root = self.__root
curr = root[1]
while curr is not root:
yield curr[2]
curr = curr[1]
def __reversed__(self):
'od.__reversed__() <==> reversed(od)'
root = self.__root
curr = root[0]
while curr is not root:
yield curr[2]
curr = curr[0]
def clear(self):
'od.clear() -> None. Remove all items from od.'
try:
for node in self.__map.itervalues():
del node[:]
root = self.__root
root[:] = [root, root, None]
self.__map.clear()
except AttributeError:
pass
dict.clear(self)
def popitem(self, last=True):
'''od.popitem() -> (k, v), return and remove a (key, value) pair.
Pairs are returned in LIFO order if last is true or FIFO order if false.
'''
if not self:
raise KeyError('dictionary is empty')
root = self.__root
if last:
link = root[0]
link_prev = link[0]
link_prev[1] = root
root[0] = link_prev
else:
link = root[1]
link_next = link[1]
root[1] = link_next
link_next[0] = root
key = link[2]
del self.__map[key]
value = dict.pop(self, key)
return key, value
# -- the following methods do not depend on the internal structure --
def keys(self):
'od.keys() -> list of keys in od'
return list(self)
def values(self):
'od.values() -> list of values in od'
return [self[key] for key in self]
def items(self):
'od.items() -> list of (key, value) pairs in od'
return [(key, self[key]) for key in self]
def iterkeys(self):
'od.iterkeys() -> an iterator over the keys in od'
return iter(self)
def itervalues(self):
'od.itervalues -> an iterator over the values in od'
for k in self:
yield self[k]
def iteritems(self):
'od.iteritems -> an iterator over the (key, value) items in od'
for k in self:
yield (k, self[k])
def update(*args, **kwds):
'''od.update(E, **F) -> None. Update od from dict/iterable E and F.
If E is a dict instance, does: for k in E: od[k] = E[k]
If E has a .keys() method, does: for k in E.keys(): od[k] = E[k]
Or if E is an iterable of items, does: for k, v in E: od[k] = v
In either case, this is followed by: for k, v in F.items(): od[k] = v
'''
if len(args) > 2:
raise TypeError('update() takes at most 2 positional '
'arguments (%d given)' % (len(args),))
elif not args:
raise TypeError('update() takes at least 1 argument (0 given)')
self = args[0]
# Make progressively weaker assumptions about "other"
other = ()
if len(args) == 2:
other = args[1]
if isinstance(other, dict):
for key in other:
self[key] = other[key]
elif hasattr(other, 'keys'):
for key in other.keys():
self[key] = other[key]
else:
for key, value in other:
self[key] = value
for key, value in kwds.items():
self[key] = value
__update = update # let subclasses override update without breaking __init__
__marker = object()
def pop(self, key, default=__marker):
'''od.pop(k[,d]) -> v, remove specified key and return the corresponding value.
If key is not found, d is returned if given, otherwise KeyError is raised.
'''
if key in self:
result = self[key]
del self[key]
return result
if default is self.__marker:
raise KeyError(key)
return default
def setdefault(self, key, default=None):
'od.setdefault(k[,d]) -> od.get(k,d), also set od[k]=d if k not in od'
if key in self:
return self[key]
self[key] = default
return default
def __repr__(self, _repr_running={}):
'od.__repr__() <==> repr(od)'
call_key = id(self), _get_ident()
if call_key in _repr_running:
return '...'
_repr_running[call_key] = 1
try:
if not self:
return '%s()' % (self.__class__.__name__,)
return '%s(%r)' % (self.__class__.__name__, self.items())
finally:
del _repr_running[call_key]
def __reduce__(self):
'Return state information for pickling'
items = [[k, self[k]] for k in self]
inst_dict = vars(self).copy()
for k in vars(OrderedDict()):
inst_dict.pop(k, None)
if inst_dict:
return (self.__class__, (items,), inst_dict)
return self.__class__, (items,)
def copy(self):
'od.copy() -> a shallow copy of od'
return self.__class__(self)
@classmethod
def fromkeys(cls, iterable, value=None):
'''OD.fromkeys(S[, v]) -> New ordered dictionary with keys from S
and values equal to v (which defaults to None).
'''
d = cls()
for key in iterable:
d[key] = value
return d
def __eq__(self, other):
'''od.__eq__(y) <==> od==y. Comparison to another OD is order-sensitive
while comparison to a regular mapping is order-insensitive.
'''
if isinstance(other, OrderedDict):
return len(self)==len(other) and self.items() == other.items()
return dict.__eq__(self, other)
def __ne__(self, other):
return not self == other
# -- the following methods are only used in Python 2.7 --
def viewkeys(self):
"od.viewkeys() -> a set-like object providing a view on od's keys"
return KeysView(self)
def viewvalues(self):
"od.viewvalues() -> an object providing a view on od's values"
return ValuesView(self)
def viewitems(self):
"od.viewitems() -> a set-like object providing a view on od's items"
return ItemsView(self)

127
lib/spack/external/py26/ordereddict.py vendored Normal file
View file

@ -0,0 +1,127 @@
# Copyright (c) 2009 Raymond Hettinger
#
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation files
# (the "Software"), to deal in the Software without restriction,
# including without limitation the rights to use, copy, modify, merge,
# publish, distribute, sublicense, and/or sell copies of the Software,
# and to permit persons to whom the Software is furnished to do so,
# subject to the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
# OTHER DEALINGS IN THE SOFTWARE.
from UserDict import DictMixin
class OrderedDict(dict, DictMixin):
def __init__(self, *args, **kwds):
if len(args) > 1:
raise TypeError('expected at most 1 arguments, got %d' % len(args))
try:
self.__end
except AttributeError:
self.clear()
self.update(*args, **kwds)
def clear(self):
self.__end = end = []
end += [None, end, end] # sentinel node for doubly linked list
self.__map = {} # key --> [key, prev, next]
dict.clear(self)
def __setitem__(self, key, value):
if key not in self:
end = self.__end
curr = end[1]
curr[2] = end[1] = self.__map[key] = [key, curr, end]
dict.__setitem__(self, key, value)
def __delitem__(self, key):
dict.__delitem__(self, key)
key, prev, next = self.__map.pop(key)
prev[2] = next
next[1] = prev
def __iter__(self):
end = self.__end
curr = end[2]
while curr is not end:
yield curr[0]
curr = curr[2]
def __reversed__(self):
end = self.__end
curr = end[1]
while curr is not end:
yield curr[0]
curr = curr[1]
def popitem(self, last=True):
if not self:
raise KeyError('dictionary is empty')
if last:
key = reversed(self).next()
else:
key = iter(self).next()
value = self.pop(key)
return key, value
def __reduce__(self):
items = [[k, self[k]] for k in self]
tmp = self.__map, self.__end
del self.__map, self.__end
inst_dict = vars(self).copy()
self.__map, self.__end = tmp
if inst_dict:
return (self.__class__, (items,), inst_dict)
return self.__class__, (items,)
def keys(self):
return list(self)
setdefault = DictMixin.setdefault
update = DictMixin.update
pop = DictMixin.pop
values = DictMixin.values
items = DictMixin.items
iterkeys = DictMixin.iterkeys
itervalues = DictMixin.itervalues
iteritems = DictMixin.iteritems
def __repr__(self):
if not self:
return '%s()' % (self.__class__.__name__,)
return '%s(%r)' % (self.__class__.__name__, self.items())
def copy(self):
return self.__class__(self)
@classmethod
def fromkeys(cls, iterable, value=None):
d = cls()
for key in iterable:
d[key] = value
return d
def __eq__(self, other):
if isinstance(other, OrderedDict):
if len(self) != len(other):
return False
for p, q in zip(self.items(), other.items()):
if p != q:
return False
return True
return dict.__eq__(self, other)
def __ne__(self, other):
return not self == other

View file

@ -60,7 +60,7 @@
import jsonschema import jsonschema
from yaml.error import MarkedYAMLError from yaml.error import MarkedYAMLError
from jsonschema import Draft4Validator, validators from jsonschema import Draft4Validator, validators
from ordereddict_backport import OrderedDict from spack.util.ordereddict import OrderedDict
import llnl.util.tty as tty import llnl.util.tty as tty
from llnl.util.filesystem import mkdirp from llnl.util.filesystem import mkdirp
@ -236,7 +236,7 @@ def __repr__(self):
def highest_precedence_scope(): def highest_precedence_scope():
"""Get the scope with highest precedence (prefs will override others).""" """Get the scope with highest precedence (prefs will override others)."""
return config_scopes.values()[-1] return list(config_scopes.values())[-1]
def validate_scope(scope): def validate_scope(scope):

View file

@ -27,7 +27,7 @@
import os import os
import tempfile import tempfile
import ordereddict_backport import spack.util.ordereddict
import pytest import pytest
import spack import spack
import spack.config import spack.config
@ -198,7 +198,7 @@ def config(tmpdir):
"""Mocks the configuration scope.""" """Mocks the configuration scope."""
spack.config.clear_config_caches() spack.config.clear_config_caches()
real_scope = spack.config.config_scopes real_scope = spack.config.config_scopes
spack.config.config_scopes = ordereddict_backport.OrderedDict() spack.config.config_scopes = spack.util.ordereddict.OrderedDict()
for priority in ['low', 'high']: for priority in ['low', 'high']:
spack.config.ConfigScope(priority, str(tmpdir.join(priority))) spack.config.ConfigScope(priority, str(tmpdir.join(priority)))
Config = collections.namedtuple('Config', ['real', 'mock']) Config = collections.namedtuple('Config', ['real', 'mock'])

View file

@ -28,7 +28,7 @@
import shutil import shutil
import re import re
import ordereddict_backport import spack.util.ordereddict
import py import py
import pytest import pytest
@ -240,7 +240,7 @@ def config(configuration_dir):
spack.package_prefs.PackagePrefs.clear_caches() spack.package_prefs.PackagePrefs.clear_caches()
spack.config.clear_config_caches() spack.config.clear_config_caches()
real_scope = spack.config.config_scopes real_scope = spack.config.config_scopes
spack.config.config_scopes = ordereddict_backport.OrderedDict() spack.config.config_scopes = spack.util.ordereddict.OrderedDict()
spack.config.ConfigScope('site', str(configuration_dir.join('site'))) spack.config.ConfigScope('site', str(configuration_dir.join('site')))
spack.config.ConfigScope('system', str(configuration_dir.join('system'))) spack.config.ConfigScope('system', str(configuration_dir.join('system')))
spack.config.ConfigScope('user', str(configuration_dir.join('user'))) spack.config.ConfigScope('user', str(configuration_dir.join('user')))
@ -644,7 +644,7 @@ def __init__(self, name, dependencies, dependency_types, conditions=None,
versions=None): versions=None):
self.name = name self.name = name
self.spec = None self.spec = None
self.dependencies = ordereddict_backport.OrderedDict() self.dependencies = spack.util.ordereddict.OrderedDict()
assert len(dependencies) == len(dependency_types) assert len(dependencies) == len(dependency_types)
for dep, dtype in zip(dependencies, dependency_types): for dep, dtype in zip(dependencies, dependency_types):

View file

@ -68,10 +68,10 @@ def test_parse(data):
def test_dict_order(data): def test_dict_order(data):
expected_order = ['x86_64', 'some_list', 'another_list', 'some_key'] expected_order = ['x86_64', 'some_list', 'another_list', 'some_key']
assert data['config_file'].keys() == expected_order assert list(data['config_file'].keys()) == expected_order
expected_order = ['foo', 'bar', 'baz'] expected_order = ['foo', 'bar', 'baz']
assert data['config_file']['x86_64'].keys() == expected_order assert list(data['config_file']['x86_64'].keys()) == expected_order
def test_line_numbers(data): def test_line_numbers(data):

View file

@ -0,0 +1,40 @@
##############################################################################
# Copyright (c) 2013-2017, Lawrence Livermore National Security, LLC.
# Produced at the Lawrence Livermore National Laboratory.
#
# This file is part of Spack.
# Created by Todd Gamblin, tgamblin@llnl.gov, All rights reserved.
# LLNL-CODE-647188
#
# For details, see https://github.com/spack/spack
# Please also see the NOTICE and LICENSE files for our notice and the LGPL.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License (as
# published by the Free Software Foundation) version 2.1, February 1999.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the IMPLIED WARRANTY OF
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the terms and
# conditions of the GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
##############################################################################
"""This file dispatches to the correct implementation of OrderedDict."""
# TODO: this file can be removed when support for python 2.6 will be dropped
# Removing this import will make python 2.6
# fail on import of ordereddict
from __future__ import absolute_import
import sys
if sys.version_info[:2] == (2, 6):
import ordereddict
OrderedDict = ordereddict.OrderedDict
else:
import collections
OrderedDict = collections.OrderedDict

View file

@ -35,7 +35,7 @@
from yaml import Loader, Dumper from yaml import Loader, Dumper
from yaml.nodes import MappingNode, SequenceNode, ScalarNode from yaml.nodes import MappingNode, SequenceNode, ScalarNode
from yaml.constructor import ConstructorError from yaml.constructor import ConstructorError
from ordereddict_backport import OrderedDict from spack.util.ordereddict import OrderedDict
import spack.error import spack.error