base cmd module finished
This commit is contained in:
9
.gitignore
vendored
9
.gitignore
vendored
@@ -9,18 +9,10 @@ __pycache__/
|
|||||||
|
|
||||||
# Distribution / packaging
|
# Distribution / packaging
|
||||||
.Python
|
.Python
|
||||||
build/
|
|
||||||
develop-eggs/
|
develop-eggs/
|
||||||
dist/
|
|
||||||
downloads/
|
|
||||||
eggs/
|
eggs/
|
||||||
.eggs/
|
.eggs/
|
||||||
lib/
|
|
||||||
lib64/
|
|
||||||
parts/
|
|
||||||
sdist/
|
sdist/
|
||||||
var/
|
|
||||||
wheels/
|
|
||||||
share/python-wheels/
|
share/python-wheels/
|
||||||
*.egg-info/
|
*.egg-info/
|
||||||
.installed.cfg
|
.installed.cfg
|
||||||
@@ -145,7 +137,6 @@ venv.bak/
|
|||||||
.ropeproject
|
.ropeproject
|
||||||
|
|
||||||
# mkdocs documentation
|
# mkdocs documentation
|
||||||
/site
|
|
||||||
|
|
||||||
# mypy
|
# mypy
|
||||||
.mypy_cache/
|
.mypy_cache/
|
||||||
|
|||||||
49
bin/sunhpc
Executable file
49
bin/sunhpc
Executable file
@@ -0,0 +1,49 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# -*- python -*-
|
||||||
|
#
|
||||||
|
# Copyright Sunhpc Project Developers. See COPYRIGHT file for details.
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
|
||||||
|
|
||||||
|
# This file is bilingual. The following shell code finds our preferred python.
|
||||||
|
# Following line is a shell no-op, and starts a multi-line Python comment.
|
||||||
|
# See https://stackoverflow.com/a/47886254
|
||||||
|
""":"
|
||||||
|
# prefer SUNHPC_PYTHON environment variable, python3, python, then python2
|
||||||
|
SUNHPC_PREFERRED_PYTHONS="python3 python python2 /usr/libexec/platform-python"
|
||||||
|
for cmd in "${SUNHPC_PYTHON:-}" ${SUNHPC_PREFERRED_PYTHONS}; do
|
||||||
|
if command -v > /dev/null "$cmd"; then
|
||||||
|
export SUNHPC_PYTHON="$(command -v "$cmd")"
|
||||||
|
exec "${SUNHPC_PYTHON}" "$0" "$@"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "==> Error: sunhpc could not find a python interpreter!" >&2
|
||||||
|
exit 1
|
||||||
|
":"""
|
||||||
|
# Line above is a shell no-op, and ends a python multi-line comment.
|
||||||
|
# The code above runs this file with our preferred python interpreter.
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
min_python3 = (3, 6)
|
||||||
|
|
||||||
|
if sys.version_info[:2] < min_python3:
|
||||||
|
v_info = sys.version_info[:3]
|
||||||
|
msg = "Sunhpc requires Python %d.%d or higher " % min_python3
|
||||||
|
msg += "You are running sunhpc with Python %d.%d.%d." % v_info
|
||||||
|
sys.exit(msg)
|
||||||
|
|
||||||
|
# Find sunhpc's location and its prefix.
|
||||||
|
sunhpc_file = os.path.realpath(os.path.expanduser(__file__))
|
||||||
|
sunhpc_prefix = os.path.dirname(os.path.dirname(sunhpc_file))
|
||||||
|
|
||||||
|
# Allow sunhpc libs to be imported in our scripts
|
||||||
|
sys.path.insert(0, os.path.join(sunhpc_prefix, "lib", "sunhpc"))
|
||||||
|
|
||||||
|
from sunhpc.main import main # noqa: E402
|
||||||
|
|
||||||
|
# Once we've set up the system path, run the sunhpc main method
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sys.exit(main(sys.argv))
|
||||||
33
lib/sunhpc/_yaml/__init__.py
Normal file
33
lib/sunhpc/_yaml/__init__.py
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
# This is a stub package designed to roughly emulate the _yaml
|
||||||
|
# extension module, which previously existed as a standalone module
|
||||||
|
# and has been moved into the `yaml` package namespace.
|
||||||
|
# It does not perfectly mimic its old counterpart, but should get
|
||||||
|
# close enough for anyone who's relying on it even when they shouldn't.
|
||||||
|
import yaml
|
||||||
|
|
||||||
|
# in some circumstances, the yaml module we imoprted may be from a different version, so we need
|
||||||
|
# to tread carefully when poking at it here (it may not have the attributes we expect)
|
||||||
|
if not getattr(yaml, '__with_libyaml__', False):
|
||||||
|
from sys import version_info
|
||||||
|
|
||||||
|
exc = ModuleNotFoundError if version_info >= (3, 6) else ImportError
|
||||||
|
raise exc("No module named '_yaml'")
|
||||||
|
else:
|
||||||
|
from yaml._yaml import *
|
||||||
|
import warnings
|
||||||
|
warnings.warn(
|
||||||
|
'The _yaml extension module is now located at yaml._yaml'
|
||||||
|
' and its location is subject to change. To use the'
|
||||||
|
' LibYAML-based parser and emitter, import from `yaml`:'
|
||||||
|
' `from yaml import CLoader as Loader, CDumper as Dumper`.',
|
||||||
|
DeprecationWarning
|
||||||
|
)
|
||||||
|
del warnings
|
||||||
|
# Don't `del yaml` here because yaml is actually an existing
|
||||||
|
# namespace member of _yaml.
|
||||||
|
|
||||||
|
__name__ = '_yaml'
|
||||||
|
# If the module is top-level (i.e. not a part of any specific package)
|
||||||
|
# then the attribute should be set to ''.
|
||||||
|
# https://docs.python.org/3.8/library/types.html
|
||||||
|
__package__ = ''
|
||||||
1
lib/sunhpc/pyyaml-6.0.3.dist-info/INSTALLER
Normal file
1
lib/sunhpc/pyyaml-6.0.3.dist-info/INSTALLER
Normal file
@@ -0,0 +1 @@
|
|||||||
|
pip
|
||||||
59
lib/sunhpc/pyyaml-6.0.3.dist-info/METADATA
Normal file
59
lib/sunhpc/pyyaml-6.0.3.dist-info/METADATA
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
Metadata-Version: 2.4
|
||||||
|
Name: PyYAML
|
||||||
|
Version: 6.0.3
|
||||||
|
Summary: YAML parser and emitter for Python
|
||||||
|
Home-page: https://pyyaml.org/
|
||||||
|
Download-URL: https://pypi.org/project/PyYAML/
|
||||||
|
Author: Kirill Simonov
|
||||||
|
Author-email: xi@resolvent.net
|
||||||
|
License: MIT
|
||||||
|
Project-URL: Bug Tracker, https://github.com/yaml/pyyaml/issues
|
||||||
|
Project-URL: CI, https://github.com/yaml/pyyaml/actions
|
||||||
|
Project-URL: Documentation, https://pyyaml.org/wiki/PyYAMLDocumentation
|
||||||
|
Project-URL: Mailing lists, http://lists.sourceforge.net/lists/listinfo/yaml-core
|
||||||
|
Project-URL: Source Code, https://github.com/yaml/pyyaml
|
||||||
|
Platform: Any
|
||||||
|
Classifier: Development Status :: 5 - Production/Stable
|
||||||
|
Classifier: Intended Audience :: Developers
|
||||||
|
Classifier: License :: OSI Approved :: MIT License
|
||||||
|
Classifier: Operating System :: OS Independent
|
||||||
|
Classifier: Programming Language :: Cython
|
||||||
|
Classifier: Programming Language :: Python
|
||||||
|
Classifier: Programming Language :: Python :: 3
|
||||||
|
Classifier: Programming Language :: Python :: 3.8
|
||||||
|
Classifier: Programming Language :: Python :: 3.9
|
||||||
|
Classifier: Programming Language :: Python :: 3.10
|
||||||
|
Classifier: Programming Language :: Python :: 3.11
|
||||||
|
Classifier: Programming Language :: Python :: 3.12
|
||||||
|
Classifier: Programming Language :: Python :: 3.13
|
||||||
|
Classifier: Programming Language :: Python :: 3.14
|
||||||
|
Classifier: Programming Language :: Python :: Implementation :: CPython
|
||||||
|
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
||||||
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
||||||
|
Classifier: Topic :: Text Processing :: Markup
|
||||||
|
Requires-Python: >=3.8
|
||||||
|
License-File: LICENSE
|
||||||
|
Dynamic: author
|
||||||
|
Dynamic: author-email
|
||||||
|
Dynamic: classifier
|
||||||
|
Dynamic: description
|
||||||
|
Dynamic: download-url
|
||||||
|
Dynamic: home-page
|
||||||
|
Dynamic: license
|
||||||
|
Dynamic: license-file
|
||||||
|
Dynamic: platform
|
||||||
|
Dynamic: project-url
|
||||||
|
Dynamic: requires-python
|
||||||
|
Dynamic: summary
|
||||||
|
|
||||||
|
YAML is a data serialization format designed for human readability
|
||||||
|
and interaction with scripting languages. PyYAML is a YAML parser
|
||||||
|
and emitter for Python.
|
||||||
|
|
||||||
|
PyYAML features a complete YAML 1.1 parser, Unicode support, pickle
|
||||||
|
support, capable extension API, and sensible error messages. PyYAML
|
||||||
|
supports standard YAML tags and provides Python-specific tags that
|
||||||
|
allow to represent an arbitrary Python object.
|
||||||
|
|
||||||
|
PyYAML is applicable for a broad range of tasks from complex
|
||||||
|
configuration files to object serialization and persistence.
|
||||||
44
lib/sunhpc/pyyaml-6.0.3.dist-info/RECORD
Normal file
44
lib/sunhpc/pyyaml-6.0.3.dist-info/RECORD
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
_yaml/__init__.py,sha256=04Ae_5osxahpJHa3XBZUAf4wi6XX32gR8D6X6p64GEA,1402
|
||||||
|
_yaml/__pycache__/__init__.cpython-39.pyc,,
|
||||||
|
pyyaml-6.0.3.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
|
||||||
|
pyyaml-6.0.3.dist-info/METADATA,sha256=A8O0Fe040J-u3Ek2DpMHabQMWPaFhebeAOLkkWqFjTQ,2351
|
||||||
|
pyyaml-6.0.3.dist-info/RECORD,,
|
||||||
|
pyyaml-6.0.3.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
||||||
|
pyyaml-6.0.3.dist-info/WHEEL,sha256=5iXtvL-Ee6MxEei7h3rrF-PfLYYJzI7hBjqY9zzRE3g,184
|
||||||
|
pyyaml-6.0.3.dist-info/licenses/LICENSE,sha256=jTko-dxEkP1jVwfLiOsmvXZBAqcoKVQwfT5RZ6V36KQ,1101
|
||||||
|
pyyaml-6.0.3.dist-info/top_level.txt,sha256=rpj0IVMTisAjh_1vG3Ccf9v5jpCQwAz6cD1IVU5ZdhQ,11
|
||||||
|
yaml/__init__.py,sha256=sZ38wzPWp139cwc5ARZFByUvJxtB07X32FUQAzoFR6c,12311
|
||||||
|
yaml/__pycache__/__init__.cpython-39.pyc,,
|
||||||
|
yaml/__pycache__/composer.cpython-39.pyc,,
|
||||||
|
yaml/__pycache__/constructor.cpython-39.pyc,,
|
||||||
|
yaml/__pycache__/cyaml.cpython-39.pyc,,
|
||||||
|
yaml/__pycache__/dumper.cpython-39.pyc,,
|
||||||
|
yaml/__pycache__/emitter.cpython-39.pyc,,
|
||||||
|
yaml/__pycache__/error.cpython-39.pyc,,
|
||||||
|
yaml/__pycache__/events.cpython-39.pyc,,
|
||||||
|
yaml/__pycache__/loader.cpython-39.pyc,,
|
||||||
|
yaml/__pycache__/nodes.cpython-39.pyc,,
|
||||||
|
yaml/__pycache__/parser.cpython-39.pyc,,
|
||||||
|
yaml/__pycache__/reader.cpython-39.pyc,,
|
||||||
|
yaml/__pycache__/representer.cpython-39.pyc,,
|
||||||
|
yaml/__pycache__/resolver.cpython-39.pyc,,
|
||||||
|
yaml/__pycache__/scanner.cpython-39.pyc,,
|
||||||
|
yaml/__pycache__/serializer.cpython-39.pyc,,
|
||||||
|
yaml/__pycache__/tokens.cpython-39.pyc,,
|
||||||
|
yaml/_yaml.cpython-39-x86_64-linux-gnu.so,sha256=LKsWBOWDOt5aEBBdplzqKWy6cNEikmY6jjMOEuqVJic,2445280
|
||||||
|
yaml/composer.py,sha256=_Ko30Wr6eDWUeUpauUGT3Lcg9QPBnOPVlTnIMRGJ9FM,4883
|
||||||
|
yaml/constructor.py,sha256=kNgkfaeLUkwQYY_Q6Ff1Tz2XVw_pG1xVE9Ak7z-viLA,28639
|
||||||
|
yaml/cyaml.py,sha256=6ZrAG9fAYvdVe2FK_w0hmXoG7ZYsoYUwapG8CiC72H0,3851
|
||||||
|
yaml/dumper.py,sha256=PLctZlYwZLp7XmeUdwRuv4nYOZ2UBnDIUy8-lKfLF-o,2837
|
||||||
|
yaml/emitter.py,sha256=jghtaU7eFwg31bG0B7RZea_29Adi9CKmXq_QjgQpCkQ,43006
|
||||||
|
yaml/error.py,sha256=Ah9z-toHJUbE9j-M8YpxgSRM5CgLCcwVzJgLLRF2Fxo,2533
|
||||||
|
yaml/events.py,sha256=50_TksgQiE4up-lKo_V-nBy-tAIxkIPQxY5qDhKCeHw,2445
|
||||||
|
yaml/loader.py,sha256=UVa-zIqmkFSCIYq_PgSGm4NSJttHY2Rf_zQ4_b1fHN0,2061
|
||||||
|
yaml/nodes.py,sha256=gPKNj8pKCdh2d4gr3gIYINnPOaOxGhJAUiYhGRnPE84,1440
|
||||||
|
yaml/parser.py,sha256=ilWp5vvgoHFGzvOZDItFoGjD6D42nhlZrZyjAwa0oJo,25495
|
||||||
|
yaml/reader.py,sha256=0dmzirOiDG4Xo41RnuQS7K9rkY3xjHiVasfDMNTqCNw,6794
|
||||||
|
yaml/representer.py,sha256=IuWP-cAW9sHKEnS0gCqSa894k1Bg4cgTxaDwIcbRQ-Y,14190
|
||||||
|
yaml/resolver.py,sha256=9L-VYfm4mWHxUD1Vg4X7rjDRK_7VZd6b92wzq7Y2IKY,9004
|
||||||
|
yaml/scanner.py,sha256=YEM3iLZSaQwXcQRg2l2R4MdT0zGP2F9eHkKGKnHyWQY,51279
|
||||||
|
yaml/serializer.py,sha256=ChuFgmhU01hj4xgI8GaKv6vfM2Bujwa9i7d2FAHj7cA,4165
|
||||||
|
yaml/tokens.py,sha256=lTQIzSVw8Mg9wv459-TjiOQe6wVziqaRlqX2_89rp54,2573
|
||||||
0
lib/sunhpc/pyyaml-6.0.3.dist-info/REQUESTED
Normal file
0
lib/sunhpc/pyyaml-6.0.3.dist-info/REQUESTED
Normal file
7
lib/sunhpc/pyyaml-6.0.3.dist-info/WHEEL
Normal file
7
lib/sunhpc/pyyaml-6.0.3.dist-info/WHEEL
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
Wheel-Version: 1.0
|
||||||
|
Generator: setuptools (80.9.0)
|
||||||
|
Root-Is-Purelib: false
|
||||||
|
Tag: cp39-cp39-manylinux_2_17_x86_64
|
||||||
|
Tag: cp39-cp39-manylinux2014_x86_64
|
||||||
|
Tag: cp39-cp39-manylinux_2_28_x86_64
|
||||||
|
|
||||||
20
lib/sunhpc/pyyaml-6.0.3.dist-info/licenses/LICENSE
Normal file
20
lib/sunhpc/pyyaml-6.0.3.dist-info/licenses/LICENSE
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
Copyright (c) 2017-2021 Ingy döt Net
|
||||||
|
Copyright (c) 2006-2016 Kirill Simonov
|
||||||
|
|
||||||
|
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.
|
||||||
2
lib/sunhpc/pyyaml-6.0.3.dist-info/top_level.txt
Normal file
2
lib/sunhpc/pyyaml-6.0.3.dist-info/top_level.txt
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
_yaml
|
||||||
|
yaml
|
||||||
97
lib/sunhpc/sunhpc/__init__.py
Normal file
97
lib/sunhpc/sunhpc/__init__.py
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
# Copyright Sunhpc Project Developers. See COPYRIGHT file for details.
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
|
||||||
|
|
||||||
|
import functools
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
import sunhpc.paths
|
||||||
|
|
||||||
|
#: PEP440 canonical <major>.<minor>.<micro>.<devN> string
|
||||||
|
__version__ = "1.0.0.dev0"
|
||||||
|
sunhpc_version = __version__
|
||||||
|
|
||||||
|
#: The current Package API version implemented by this version of Sunhpc. The Package API defines
|
||||||
|
#: the Python interface for packages as well as the layout of package repositories. The minor
|
||||||
|
#: version is incremented when the package API is extended in a backwards-compatible way. The major
|
||||||
|
#: version is incremented upon breaking changes. This version is changed independently from the
|
||||||
|
#: Sunhpc version.
|
||||||
|
package_api_version = (2, 4)
|
||||||
|
|
||||||
|
#: The minimum Package API version that this version of Sunhpc is compatible with. This should
|
||||||
|
#: always be a tuple of the form ``(major, 0)``, since compatibility with vX.Y implies
|
||||||
|
#: compatibility with vX.0.
|
||||||
|
min_package_api_version = (1, 0)
|
||||||
|
|
||||||
|
|
||||||
|
def __try_int(v):
|
||||||
|
try:
|
||||||
|
return int(v)
|
||||||
|
except ValueError:
|
||||||
|
return v
|
||||||
|
|
||||||
|
|
||||||
|
#: (major, minor, micro, dev release) tuple
|
||||||
|
sunhpc_version_info = tuple([__try_int(v) for v in __version__.split(".")])
|
||||||
|
|
||||||
|
|
||||||
|
@functools.lru_cache(maxsize=None)
|
||||||
|
def get_sunhpc_commit() -> Optional[str]:
|
||||||
|
"""Get the Sunhpc git commit sha.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
(str or None) the commit sha if available, otherwise None
|
||||||
|
"""
|
||||||
|
git_path = os.path.join(sunhpc.paths.prefix, ".git")
|
||||||
|
if not os.path.exists(git_path):
|
||||||
|
return None
|
||||||
|
|
||||||
|
git = sunhpc.util.git.git()
|
||||||
|
if not git:
|
||||||
|
return None
|
||||||
|
|
||||||
|
rev = git(
|
||||||
|
"-C",
|
||||||
|
sunhpc.paths.prefix,
|
||||||
|
"rev-parse",
|
||||||
|
"HEAD",
|
||||||
|
output=str,
|
||||||
|
error=os.devnull,
|
||||||
|
fail_on_error=False,
|
||||||
|
)
|
||||||
|
if git.returncode != 0:
|
||||||
|
return None
|
||||||
|
|
||||||
|
match = re.match(r"[a-f\d]{7,}$", rev)
|
||||||
|
return match.group(0) if match else None
|
||||||
|
|
||||||
|
|
||||||
|
def get_version() -> str:
|
||||||
|
"""Get a descriptive version of this instance of Sunhpc.
|
||||||
|
|
||||||
|
Outputs ``"<PEP440 version> (<git commit sha>)"``.
|
||||||
|
|
||||||
|
The commit sha is only added when available.
|
||||||
|
"""
|
||||||
|
commit = get_sunhpc_commit()
|
||||||
|
if commit:
|
||||||
|
return f"{sunhpc_version} ({commit})"
|
||||||
|
return sunhpc_version
|
||||||
|
|
||||||
|
|
||||||
|
def get_short_version() -> str:
|
||||||
|
"""Short Sunhpc version."""
|
||||||
|
return f"{sunhpc_version_info[0]}.{sunhpc_version_info[1]}"
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"sunhpc_version_info",
|
||||||
|
"sunhpc_version",
|
||||||
|
"get_version",
|
||||||
|
"get_sunhpc_commit",
|
||||||
|
"get_short_version",
|
||||||
|
"package_api_version",
|
||||||
|
"min_package_api_version",
|
||||||
|
]
|
||||||
692
lib/sunhpc/sunhpc/commands/__init__.py
Normal file
692
lib/sunhpc/sunhpc/commands/__init__.py
Normal file
@@ -0,0 +1,692 @@
|
|||||||
|
import os
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
import pwd
|
||||||
|
import xml
|
||||||
|
import syslog
|
||||||
|
import sunhpc.util
|
||||||
|
|
||||||
|
from xml.sax import saxutils
|
||||||
|
from xml.sax import handler
|
||||||
|
from xml.sax import make_parser
|
||||||
|
|
||||||
|
def Abort(message, doExit=1):
|
||||||
|
'''Print a standard error message and throw a
|
||||||
|
sunhpc.util.CommandError
|
||||||
|
'''
|
||||||
|
syslog.syslog(syslog.LOG_ERR, message)
|
||||||
|
raise sunhpc.util.CommandError(message)
|
||||||
|
|
||||||
|
class DocStringHandler(
|
||||||
|
handler.ContentHandler, handler.DTDHandler,
|
||||||
|
handler.EntityResolver, handler.ErrorHandler):
|
||||||
|
|
||||||
|
def __init__(self, name='', users=[]):
|
||||||
|
handler.ContentHandler.__init__(self)
|
||||||
|
self.text = ''
|
||||||
|
self.name = name
|
||||||
|
self.users = users
|
||||||
|
self.section = {}
|
||||||
|
self.section['description'] = ''
|
||||||
|
self.section['arg'] = []
|
||||||
|
self.section['param'] = []
|
||||||
|
self.section['example'] = []
|
||||||
|
self.section['related'] = []
|
||||||
|
self.parser = make_parser()
|
||||||
|
self.parser.setContentHandler(self)
|
||||||
|
|
||||||
|
def getDocbookText(self):
|
||||||
|
s = ''
|
||||||
|
s += '<section id="sunhpc-%s" xreflabel="%s">\n' % \
|
||||||
|
('-'.join(self.name.split(' ')), self.name)
|
||||||
|
s += '<title>%s</title>\n' % self.name
|
||||||
|
s += '<cmdsynopsis>\n'
|
||||||
|
s += '\t<command>sunhpc %s</command>\n' % self.name
|
||||||
|
for ((name, type, opt, rep), txt) in self.section['arg']:
|
||||||
|
if opt:
|
||||||
|
choice = 'opt'
|
||||||
|
else:
|
||||||
|
choice = 'req'
|
||||||
|
if rep:
|
||||||
|
repeat = 'repeat'
|
||||||
|
else:
|
||||||
|
repeat = 'norepeat'
|
||||||
|
s += '\t<arg rep="%s" choice="%s">%s</arg>\n' % \
|
||||||
|
(repeat, choice, name)
|
||||||
|
for ((name, type, opt, rep), txt) in self.section['param']:
|
||||||
|
if opt:
|
||||||
|
choice = 'opt'
|
||||||
|
else:
|
||||||
|
choice = 'req'
|
||||||
|
if rep:
|
||||||
|
repeat = 'repeat'
|
||||||
|
else:
|
||||||
|
repeat = 'norepeat'
|
||||||
|
s += '\t<arg rep="%s" choice="%s">' % (repeat, choice)
|
||||||
|
s += '%s=<replaceable>%s</replaceable>' % (name, type)
|
||||||
|
s += '</arg>\n'
|
||||||
|
s += '</cmdsynopsis>\n'
|
||||||
|
s += '<para>\n'
|
||||||
|
s += saxutils.escape(self.section['description'])
|
||||||
|
s += '\n</para>\n'
|
||||||
|
if self.section['arg']:
|
||||||
|
s += '<variablelist><title>arguments</title>\n'
|
||||||
|
for ((name, type, opt, rep), txt) in \
|
||||||
|
self.section['arg']:
|
||||||
|
s += '\t<varlistentry>\n'
|
||||||
|
if opt:
|
||||||
|
term = '<optional>%s</optional>' % name
|
||||||
|
else:
|
||||||
|
term = name
|
||||||
|
s += '\t<term>%s</term>\n' % term
|
||||||
|
s += '\t<listitem>\n'
|
||||||
|
s += '\t<para>\n'
|
||||||
|
s += saxutils.escape(txt)
|
||||||
|
s += '\n\t</para>\n'
|
||||||
|
s += '\t</listitem>\n'
|
||||||
|
s += '\t</varlistentry>\n'
|
||||||
|
s += '</variablelist>\n'
|
||||||
|
if self.section['param']:
|
||||||
|
s += '<variablelist><title>parameters</title>\n'
|
||||||
|
for ((name, type, opt, rep), txt) in \
|
||||||
|
self.section['param']:
|
||||||
|
s += '\t<varlistentry>\n'
|
||||||
|
if opt:
|
||||||
|
optStart = '<optional>'
|
||||||
|
optEnd = '</optional>'
|
||||||
|
else:
|
||||||
|
optStart = ''
|
||||||
|
optEnd = ''
|
||||||
|
key = '%s=' % name
|
||||||
|
val = '<replaceable>%s</replaceable>' % type
|
||||||
|
s += '\t<term>%s%s%s%s</term>\n' % \
|
||||||
|
(optStart, key, val, optEnd)
|
||||||
|
s += '\t<listitem>\n'
|
||||||
|
s += '\t<para>\n'
|
||||||
|
s += saxutils.escape(txt)
|
||||||
|
s += '\n\t</para>\n'
|
||||||
|
s += '\t</listitem>\n'
|
||||||
|
s += '\t</varlistentry>\n'
|
||||||
|
s += '</variablelist>\n'
|
||||||
|
if self.section['example']:
|
||||||
|
s += '<variablelist><title>examples</title>\n'
|
||||||
|
for (cmd, txt) in self.section['example']:
|
||||||
|
s += '\t<varlistentry>\n'
|
||||||
|
s += '\t<term>\n'
|
||||||
|
if 'root' in self.users:
|
||||||
|
s += '# '
|
||||||
|
else:
|
||||||
|
s += '$ '
|
||||||
|
s += 'sunhpc %s' % cmd
|
||||||
|
s += '\n\t</term>\n'
|
||||||
|
s += '\t<listitem>\n'
|
||||||
|
s += '\t<para>\n'
|
||||||
|
s += saxutils.escape(txt)
|
||||||
|
s += '\n\t</para>\n'
|
||||||
|
s += '\t</listitem>\n'
|
||||||
|
s += '\t</varlistentry>\n'
|
||||||
|
s += '</variablelist>\n'
|
||||||
|
if self.section['related']:
|
||||||
|
s += '<variablelist><title>related commands</title>\n'
|
||||||
|
for related in self.section['related']:
|
||||||
|
s += '\t<varlistentry>\n'
|
||||||
|
s += '\t<term>'
|
||||||
|
s += '<xref linkend="sunhpc-%s">' % '-'.join(related.split(' '))
|
||||||
|
s += '</term>\n'
|
||||||
|
s += '\t<listitem>\n'
|
||||||
|
s += '\t<para>\n'
|
||||||
|
s += '\n\t</para>\n'
|
||||||
|
s += '\t</listitem>\n'
|
||||||
|
s += '\t</varlistentry>\n'
|
||||||
|
s += '</variablelist>\n'
|
||||||
|
s += '</section>'
|
||||||
|
return s
|
||||||
|
|
||||||
|
def getUsageText(self):
|
||||||
|
s = ''
|
||||||
|
for ((name, type, opt, rep), txt) in self.section['arg']:
|
||||||
|
if opt:
|
||||||
|
s += '[%s]' % name
|
||||||
|
else:
|
||||||
|
s += '{%s}' % name
|
||||||
|
if rep:
|
||||||
|
s += '...'
|
||||||
|
s += ' '
|
||||||
|
for ((name, type, opt, rep), txt) in self.section['param']:
|
||||||
|
if opt:
|
||||||
|
s += '[%s=%s]' % (name, type)
|
||||||
|
else:
|
||||||
|
s += '{%s=%s}' % (name, type)
|
||||||
|
if rep:
|
||||||
|
s += '...'
|
||||||
|
s += ' '
|
||||||
|
if s and s[-1] == ' ':
|
||||||
|
return s[:-1]
|
||||||
|
else:
|
||||||
|
return s
|
||||||
|
|
||||||
|
def getSphinxText(self):
|
||||||
|
if 'root' in self.users:
|
||||||
|
prompt = '#'
|
||||||
|
else:
|
||||||
|
prompt = '$'
|
||||||
|
|
||||||
|
s = ':orphan:\n\n'
|
||||||
|
s += '%s\n' % self.name
|
||||||
|
s += '%s\n\n' % ("-" * len(self.name))
|
||||||
|
s += '.. role:: defn\n\n'
|
||||||
|
utxt = self.getUsageText()
|
||||||
|
if len(utxt):
|
||||||
|
s += ':defn:`sunhpc %s` *%s*\n' % (self.name, utxt)
|
||||||
|
else:
|
||||||
|
s += ':defn:`sunhpc %s` %s\n' % (self.name, utxt)
|
||||||
|
s += '\n\n**Description:**\n'
|
||||||
|
s += self.section['description'].replace('\t',' ')
|
||||||
|
if self.section['arg']:
|
||||||
|
s += '\n**Arguments:**\n\n'
|
||||||
|
for ((name, type, opt, rep), txt) in \
|
||||||
|
self.section['arg']:
|
||||||
|
if opt:
|
||||||
|
s += '*[%s]*' % name
|
||||||
|
else:
|
||||||
|
s += '*{%s}*' % name
|
||||||
|
txt = txt.replace('*', '\*')
|
||||||
|
s += '\n%s\n' % txt.replace('\t', ' ')
|
||||||
|
if self.section['param']:
|
||||||
|
s += '\n**Parameters:**\n\n'
|
||||||
|
for ((name, type, opt, rep), txt) in \
|
||||||
|
self.section['param']:
|
||||||
|
if opt:
|
||||||
|
s += '*[%s=%s]*' % (name, type)
|
||||||
|
else:
|
||||||
|
s += '*{%s=%s}*' % (name, type)
|
||||||
|
txt = txt.replace('*', '\*')
|
||||||
|
s += '\n%s\n' % txt.replace('\t', ' ')
|
||||||
|
if self.section['example']:
|
||||||
|
s += '\n**Examples:**\n'
|
||||||
|
for (cmd, txt) in self.section['example']:
|
||||||
|
txt = txt.replace('*', '\*')
|
||||||
|
s += '%s::\n\n' % txt.replace('\t',' ')
|
||||||
|
s += ' %s sunhpc %s\n' % (prompt, cmd)
|
||||||
|
if self.section['related']:
|
||||||
|
s += '\n**Related Commands:**\n\n'
|
||||||
|
for related in self.section['related']:
|
||||||
|
s += ' * :ref:`sunhpc-%s`\n' % related.replace(' ','-')
|
||||||
|
|
||||||
|
word = self.name.split()[0]
|
||||||
|
s += '\n:ref:`%s commands <%s-ref>`\n' % (word, word)
|
||||||
|
return s
|
||||||
|
|
||||||
|
def getPlainText(self):
|
||||||
|
if 'root' in self.users:
|
||||||
|
prompt = '#'
|
||||||
|
else:
|
||||||
|
prompt = '$'
|
||||||
|
s = ''
|
||||||
|
s += ' \n%s sunhpc %s %s' % (prompt, self.name, self.getUsageText())
|
||||||
|
s += '\n\nDescription:\n'
|
||||||
|
s += self.section['description']
|
||||||
|
if self.section['arg']:
|
||||||
|
s += '\nArguments:\n'
|
||||||
|
for ((name, type, opt, rep), txt) in \
|
||||||
|
self.section['arg']:
|
||||||
|
if opt:
|
||||||
|
s += ' [%s]' % name
|
||||||
|
else:
|
||||||
|
s += ' {%s}' % name
|
||||||
|
s += '%s\n' % txt
|
||||||
|
if self.section['param']:
|
||||||
|
s += '\nParameters:\n'
|
||||||
|
for ((name, type, opt, rep), txt) in \
|
||||||
|
self.section['param']:
|
||||||
|
if opt:
|
||||||
|
s += ' [%s=%s]' % (name, type)
|
||||||
|
else:
|
||||||
|
s += ' {%s=%s}' % (name, type)
|
||||||
|
s += '%s\n' % txt
|
||||||
|
if self.section['example']:
|
||||||
|
s += '\nExamples:\n\n'
|
||||||
|
for (cmd, txt) in self.section['example']:
|
||||||
|
s += ' %s sunhpc %s' % (prompt, cmd)
|
||||||
|
s += '%s\n' % txt
|
||||||
|
if self.section['related']:
|
||||||
|
s += '\nRelated Commands:\n\n'
|
||||||
|
for related in self.section['related']:
|
||||||
|
s += ' sunhpc %s\n' % related.replace(' ','-')
|
||||||
|
return s
|
||||||
|
|
||||||
|
def getParsedText(self):
|
||||||
|
return '%s' % self.section
|
||||||
|
|
||||||
|
def startElement(self, name, attrs):
|
||||||
|
if not self.section['description']:
|
||||||
|
self.section['description'] = self.text
|
||||||
|
self.key = None
|
||||||
|
self.text = ''
|
||||||
|
if name in [ 'arg', 'param' ]:
|
||||||
|
try:
|
||||||
|
dtype = attrs.get('type')
|
||||||
|
except:
|
||||||
|
dtype = 'string'
|
||||||
|
try:
|
||||||
|
optional = int(attrs.get('optional'))
|
||||||
|
except:
|
||||||
|
if name == 'arg':
|
||||||
|
optional = 0
|
||||||
|
if name == 'param':
|
||||||
|
optional = 1
|
||||||
|
try:
|
||||||
|
repeat = int(attrs.get('repeat'))
|
||||||
|
except:
|
||||||
|
repeat = 0
|
||||||
|
name = attrs.get('name')
|
||||||
|
self.key = (name, dtype, optional, repeat)
|
||||||
|
elif name == 'example':
|
||||||
|
self.key = attrs.get('cmd')
|
||||||
|
|
||||||
|
def endElement(self, name):
|
||||||
|
if name == 'docstring':
|
||||||
|
self.section['param'].sort()
|
||||||
|
self.section['related'].sort()
|
||||||
|
elif name in [ 'arg', 'param', 'example' ]:
|
||||||
|
self.section[name].append((self.key, self.text))
|
||||||
|
else:
|
||||||
|
if name in self.section:
|
||||||
|
self.section[name].append(self.text)
|
||||||
|
|
||||||
|
def characters(self, s):
|
||||||
|
self.text += s
|
||||||
|
|
||||||
|
class Command:
|
||||||
|
'''Base class for all Sunhpc commands the general command line form
|
||||||
|
is as follows:
|
||||||
|
|
||||||
|
sunhpc action component object [ <argname args> ... ]
|
||||||
|
action(s):
|
||||||
|
add
|
||||||
|
create
|
||||||
|
list
|
||||||
|
load
|
||||||
|
sync
|
||||||
|
'''
|
||||||
|
MustBeRoot = 1
|
||||||
|
|
||||||
|
def __init__(self, database):
|
||||||
|
self.db = database
|
||||||
|
|
||||||
|
self.text = ''
|
||||||
|
self.output = []
|
||||||
|
|
||||||
|
self.os = os.uname()[0].lower()
|
||||||
|
self.arch = os.uname()[4]
|
||||||
|
self.user = pwd.getpwuid(os.getuid()).pw_name
|
||||||
|
|
||||||
|
self._args = None
|
||||||
|
self._params = None
|
||||||
|
if 'SUNHPCDEBUG' in os.environ:
|
||||||
|
self._debug = True
|
||||||
|
else:
|
||||||
|
self._debug = False
|
||||||
|
|
||||||
|
def debug(self):
|
||||||
|
"""Return True if debug mode is enabled"""
|
||||||
|
return self._debug
|
||||||
|
def abort(self, msg):
|
||||||
|
syslog.syslog(syslog.LOG_ERR, msg.split('\n')[0])
|
||||||
|
raise sunhpc.util.CommandError(msg)
|
||||||
|
def strWordWrap(self, line, indent=''):
|
||||||
|
if 'COLUMNS' in os.environ:
|
||||||
|
cols = os.environ['COLUMNS']
|
||||||
|
else:
|
||||||
|
cols = 80
|
||||||
|
l = 0
|
||||||
|
s = ''
|
||||||
|
for word in line.split(' '):
|
||||||
|
if l + len(word) < cols:
|
||||||
|
s += '%s ' % word
|
||||||
|
l += len(word) + 1 # space
|
||||||
|
else:
|
||||||
|
s += '\n%s%s ' % (indent, word)
|
||||||
|
l += len(indent) + len(word) + 1 # space
|
||||||
|
return s
|
||||||
|
|
||||||
|
def isRootUser(self):
|
||||||
|
return os.getuid() == 0
|
||||||
|
def isApacheUser(self):
|
||||||
|
return self.user == 'apache'
|
||||||
|
|
||||||
|
def str2bool(self, s):
|
||||||
|
if s and s.upper() in [ 'ON', 'YES', 'Y', 'TRUE', '1']:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
def bool2str(self, b):
|
||||||
|
if b:
|
||||||
|
return 'yes'
|
||||||
|
else:
|
||||||
|
return 'no'
|
||||||
|
|
||||||
|
def clearText(self):
|
||||||
|
self.text = ''
|
||||||
|
def addText(self, s):
|
||||||
|
if s:
|
||||||
|
self.text += s
|
||||||
|
def getText(self):
|
||||||
|
return self.text
|
||||||
|
|
||||||
|
def beginOutput(self):
|
||||||
|
self.output = []
|
||||||
|
def addOutput(self, owner, vals):
|
||||||
|
'Append a list to the output list buffer.'
|
||||||
|
plist = [ '%s:' % owner ]
|
||||||
|
if isinstance(vals, (type([]))):
|
||||||
|
plist.extend(vals)
|
||||||
|
elif isinstance(vals, (type(()))):
|
||||||
|
for e in vals:
|
||||||
|
plist.append(e)
|
||||||
|
else:
|
||||||
|
plist.append(vals)
|
||||||
|
|
||||||
|
self.output.append(plist)
|
||||||
|
def endOutput(self, header=[], padChar='-', trimOwner=1, linesep='\n'):
|
||||||
|
if not self.output:
|
||||||
|
return
|
||||||
|
|
||||||
|
showHeader = True
|
||||||
|
if 'output-header' in self._params:
|
||||||
|
showHeader = self.str2bool(self._params['output-header'])
|
||||||
|
|
||||||
|
self.outputCols = []
|
||||||
|
if 'output-col' in self._params:
|
||||||
|
showCols = self._params['output-col'].split(',')
|
||||||
|
for i in header:
|
||||||
|
if i.lower() in showCols:
|
||||||
|
self.outputCols.append(True)
|
||||||
|
else:
|
||||||
|
self.outputCols.append(False)
|
||||||
|
|
||||||
|
if trimOwner:
|
||||||
|
owner = ''
|
||||||
|
self.startOfLine = 1
|
||||||
|
for line in self.output:
|
||||||
|
if not owner:
|
||||||
|
owner = line[0]
|
||||||
|
if not owner == line[0]:
|
||||||
|
self.startOfLine = 0
|
||||||
|
else:
|
||||||
|
self.startOfLine = 0
|
||||||
|
|
||||||
|
if header and showHeader:
|
||||||
|
plist = []
|
||||||
|
for field in header:
|
||||||
|
plist.append(field.upper())
|
||||||
|
output = [plist]
|
||||||
|
output.extend(self.output)
|
||||||
|
else:
|
||||||
|
output = self.output
|
||||||
|
|
||||||
|
colwidth = []
|
||||||
|
for line in output:
|
||||||
|
for i in range(0, len(line)):
|
||||||
|
if len(colwidth) <= i:
|
||||||
|
colwidth.append(0)
|
||||||
|
if type(line[i]) != type(''):
|
||||||
|
if line[i] == None:
|
||||||
|
itemlen = 0
|
||||||
|
else:
|
||||||
|
itemlen = len(repr(line[i]))
|
||||||
|
|
||||||
|
else:
|
||||||
|
itemlen = len(line[i])
|
||||||
|
|
||||||
|
if itemlen > colwidth[i]:
|
||||||
|
colwidth[i] = itemlen
|
||||||
|
|
||||||
|
o = ''
|
||||||
|
for line in output:
|
||||||
|
plist = []
|
||||||
|
for i in range(self.startOfLine, len(line)):
|
||||||
|
if line[i] == None:
|
||||||
|
s = ''
|
||||||
|
else:
|
||||||
|
s = str(line[i])
|
||||||
|
if padChar != '':
|
||||||
|
if s:
|
||||||
|
o = s.ljust(colwidth[i])
|
||||||
|
else:
|
||||||
|
o = ''.ljust(colwidth[i], padChar)
|
||||||
|
else:
|
||||||
|
o = s
|
||||||
|
plist.append(o)
|
||||||
|
self.addText('%s%s' % (self.outputRow(plist), linesep))
|
||||||
|
def outputRow(self, plist):
|
||||||
|
if self.outputCols:
|
||||||
|
l = []
|
||||||
|
for i in range(0, len(plist)):
|
||||||
|
if self.outputCols[i + self.startOfLine]:
|
||||||
|
l.append(plist[i])
|
||||||
|
return ' '.join(l)
|
||||||
|
else:
|
||||||
|
return ' '.join(plist)
|
||||||
|
|
||||||
|
def command(self, command, args=[]):
|
||||||
|
'''Import and run a Sunhpc command.
|
||||||
|
Returns and output string.
|
||||||
|
'''
|
||||||
|
modpath = 'sunhpc.commands.%s' % command
|
||||||
|
__import__(modpath)
|
||||||
|
mod = eval(modpath)
|
||||||
|
try:
|
||||||
|
o = getattr(mod, 'Command')
|
||||||
|
n = ' '.join(command.split('.'))
|
||||||
|
except AttributeError:
|
||||||
|
return ''
|
||||||
|
|
||||||
|
o.runWrapper(n, args)
|
||||||
|
return o.getText()
|
||||||
|
|
||||||
|
def fillParams(self, names, params=None):
|
||||||
|
'''
|
||||||
|
Returns a list of variables with either default
|
||||||
|
values of the values in the PARAMS dictionary.
|
||||||
|
|
||||||
|
: type names: list
|
||||||
|
: param names: a list of (key, default) tuples, where key
|
||||||
|
is a key name of params dictionary and default
|
||||||
|
is the default value if key in not in dict
|
||||||
|
|
||||||
|
: type params: dict
|
||||||
|
: param params: optional dictionary
|
||||||
|
|
||||||
|
For example::
|
||||||
|
(svc, comp) = self.fillParams (
|
||||||
|
('service', None),
|
||||||
|
('component', None))
|
||||||
|
|
||||||
|
Can also be written as::
|
||||||
|
(svc, comp) = self.fillParams(('service',), ('component', ))
|
||||||
|
'''
|
||||||
|
# make sure names is a list or tuple
|
||||||
|
if not isinstance(names, (type([]), type(()))):
|
||||||
|
names = [ names ]
|
||||||
|
|
||||||
|
dlist = []
|
||||||
|
for e in names:
|
||||||
|
if type(e) in [ type(()), type([]) ] \
|
||||||
|
and len(e) == 2:
|
||||||
|
tuples = ( e[0], e[1] )
|
||||||
|
else:
|
||||||
|
tuples = ( e[0], None )
|
||||||
|
dlist.append(tuples)
|
||||||
|
|
||||||
|
if not params:
|
||||||
|
params = self._params
|
||||||
|
|
||||||
|
rlist = []
|
||||||
|
for (key, default) in dlist:
|
||||||
|
if 'key' in params:
|
||||||
|
rlist.append(params[key])
|
||||||
|
else:
|
||||||
|
rlist.append(default)
|
||||||
|
return rlist
|
||||||
|
|
||||||
|
def fillPositionalArgs(self, names, params=None, args=None):
|
||||||
|
'''
|
||||||
|
The helper function will allow named parameters
|
||||||
|
to be used in lieu of positional arguments
|
||||||
|
Example::
|
||||||
|
Suppose command is of the form:
|
||||||
|
command <arg1> <arg2> <arg3>
|
||||||
|
Usually called as:
|
||||||
|
command foo bar baz
|
||||||
|
However if you name the arguments as parameters, say
|
||||||
|
arg1, arg2, arg3
|
||||||
|
Then, equivalent calls of the command are
|
||||||
|
command arg1=foo arg2=bar arg3=baz
|
||||||
|
command foo bar arg3=baz
|
||||||
|
command foo bar baz
|
||||||
|
command foo arg2=bar baz
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
paramlist = list of parameter names in the order
|
||||||
|
that their unnamed argument counterparts
|
||||||
|
apper eg. paramlist=('iface', 'mac')
|
||||||
|
params = list of parameters (e.g param=value)
|
||||||
|
args = args
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
remaining args, Filled parameters
|
||||||
|
Example::
|
||||||
|
hostlist, iface, mac = self.fillPositionalArgs(
|
||||||
|
('iface','mac'), params, args)
|
||||||
|
'''
|
||||||
|
|
||||||
|
if not isinstance(names, (type([]), type(()))):
|
||||||
|
names = [ names ]
|
||||||
|
|
||||||
|
if not params:
|
||||||
|
params = self._params
|
||||||
|
|
||||||
|
if not args:
|
||||||
|
args = self._args
|
||||||
|
|
||||||
|
plist = []
|
||||||
|
for name in names:
|
||||||
|
if name in params:
|
||||||
|
plist.append(params[name])
|
||||||
|
else:
|
||||||
|
plist.append(None)
|
||||||
|
|
||||||
|
# now walk backwards through the args and pull off
|
||||||
|
# positional arguments that have not already been set
|
||||||
|
# as a param=<parameter>
|
||||||
|
|
||||||
|
trimmedArgs = args
|
||||||
|
vars = []
|
||||||
|
plist.reverse()
|
||||||
|
for e in plist:
|
||||||
|
if not e and len(trimmedArgs):
|
||||||
|
vars.append(trimmedArgs[-1])
|
||||||
|
trimmedArgs = trimmedArgs[:-1]
|
||||||
|
else:
|
||||||
|
vars.append(e)
|
||||||
|
|
||||||
|
# need to reverse the 'vars' to get them in the correct order
|
||||||
|
# since we processed them above in reverse order
|
||||||
|
vars.reverse()
|
||||||
|
rlist = []
|
||||||
|
rlist.append(trimmedArgs)
|
||||||
|
rlist.extend(vars)
|
||||||
|
return rlist
|
||||||
|
|
||||||
|
def runWrapper(self, name, args):
|
||||||
|
if args:
|
||||||
|
command = '%s %s' % (name, ' '.join(args))
|
||||||
|
else:
|
||||||
|
command = name
|
||||||
|
|
||||||
|
syslog.syslog(syslog.LOG_INFO,
|
||||||
|
'running command: %s for user: %s' % (command, self.user))
|
||||||
|
|
||||||
|
pdict = {} # flags
|
||||||
|
plist = [] # arguments
|
||||||
|
|
||||||
|
nparams = 0
|
||||||
|
flagpattern = re.compile(r'^-[a-zA-Z0-9-_+]+=')
|
||||||
|
|
||||||
|
for arg in args:
|
||||||
|
tokens = arg.split()
|
||||||
|
if tokens[0] == 'select':
|
||||||
|
plist.append(arg)
|
||||||
|
elif flagpattern.match(arg):
|
||||||
|
(key, val) = arg.split('=', 1)
|
||||||
|
pdict[key] = val
|
||||||
|
if nparams == 0:
|
||||||
|
pdict['@SUNHPCPARAM0'] = arg
|
||||||
|
nparams += 1
|
||||||
|
else:
|
||||||
|
plist.append(arg)
|
||||||
|
|
||||||
|
if plist and plist[0] == 'help':
|
||||||
|
self.help(name, pdict)
|
||||||
|
else:
|
||||||
|
if self.MustBeRoot and not \
|
||||||
|
(self.isRootUser() or self.isApacheUser()):
|
||||||
|
self.abort('command "%s" requires root privilege' % name)
|
||||||
|
else:
|
||||||
|
self._args = plist
|
||||||
|
self._params = pdict
|
||||||
|
try:
|
||||||
|
self.run(self._params, self._args)
|
||||||
|
except sunhpc.util.HostnotfoundException as e:
|
||||||
|
if self.debug():
|
||||||
|
traceback.print_exc()
|
||||||
|
self.abort(str(e))
|
||||||
|
|
||||||
|
def run(self, flags, args):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def usage(self):
|
||||||
|
if self.__doc__:
|
||||||
|
handler = DocStringHandler()
|
||||||
|
parser = make_parser()
|
||||||
|
parser.setContentHandler(handler)
|
||||||
|
try:
|
||||||
|
parser.feed('<docstring>%s</docstring>' % self.__doc__)
|
||||||
|
except:
|
||||||
|
return '-- invalid doc string --'
|
||||||
|
return handler.getUsageText()
|
||||||
|
else:
|
||||||
|
return '-- missing doc string --'
|
||||||
|
|
||||||
|
def help(self, command, flags={}):
|
||||||
|
if not self.__doc__:
|
||||||
|
return
|
||||||
|
if self.MustBeRoot:
|
||||||
|
users = ['root', 'apache']
|
||||||
|
else:
|
||||||
|
users = []
|
||||||
|
|
||||||
|
if 'format' in flags:
|
||||||
|
format = flags['format'].lower()
|
||||||
|
else:
|
||||||
|
format = 'plain'
|
||||||
|
|
||||||
|
if format == 'raw':
|
||||||
|
i = 1
|
||||||
|
for line in self.__doc__.split('\n'):
|
||||||
|
self.addText('%d:%s\n' % (i, line))
|
||||||
|
i += 1
|
||||||
|
else:
|
||||||
|
handler = DocStringHandler(command, users)
|
||||||
|
parser = make_parser()
|
||||||
|
parser.setContentHandler(handler)
|
||||||
|
parser.feed('<docstring>%s</docstring>' % self.__doc__)
|
||||||
|
if format == 'docbook':
|
||||||
|
self.addText(handler.getDockbookText())
|
||||||
|
elif format == 'parsed':
|
||||||
|
self.addText(handler.getParsedText())
|
||||||
|
elif format == 'sphinx':
|
||||||
|
self.addText(handler.getSphinxText())
|
||||||
|
else:
|
||||||
|
self.addText(handler.getPlainText())
|
||||||
3
lib/sunhpc/sunhpc/commands/init/__init__.py
Normal file
3
lib/sunhpc/sunhpc/commands/init/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
import sunhpc.commands
|
||||||
|
class command(sunhpc.commands.Command):
|
||||||
|
MustBeRoot = 1
|
||||||
35
lib/sunhpc/sunhpc/commands/init/config/__init__.py
Normal file
35
lib/sunhpc/sunhpc/commands/init/config/__init__.py
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
import os
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
import sunhpc.commands
|
||||||
|
|
||||||
|
class Command(sunhpc.commands.init.command):
|
||||||
|
"""
|
||||||
|
这个命令是初始化系统的相关配置,并且写入到配置文件.
|
||||||
|
<arg type='string' name='iface'>
|
||||||
|
提供一个内网的接口、例如: eth1
|
||||||
|
</arg>
|
||||||
|
|
||||||
|
<arg type='string' name='mnt'>
|
||||||
|
提供一个系统ISO文件、或者挂载路径、例如: /mnt/cdrom
|
||||||
|
</arg>
|
||||||
|
|
||||||
|
<param type='string' name='iface'>
|
||||||
|
提供一个内网的接口、例如: eth1
|
||||||
|
</param>
|
||||||
|
|
||||||
|
<param type='string' name='mnt'>
|
||||||
|
提供一个系统ISO文件、或者挂载路径、例如: /mnt/cdrom
|
||||||
|
</param>
|
||||||
|
|
||||||
|
<example cmd='init config eth1 /mnt/cdrom'>
|
||||||
|
使用eth1网络接口、和 /mnt/cdrom 挂载路径
|
||||||
|
</example>
|
||||||
|
|
||||||
|
<example cmd='init config iface=eth1 mnt=/mnt/cdrom'>
|
||||||
|
使用eth1网络接口、和 /mnt/cdrom 挂载路径
|
||||||
|
</example>
|
||||||
|
"""
|
||||||
|
|
||||||
|
def run(self, params, args):
|
||||||
|
print ('This is init opt ...')
|
||||||
4
lib/sunhpc/sunhpc/commands/list/__init__.py
Normal file
4
lib/sunhpc/sunhpc/commands/list/__init__.py
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
import sunhpc.commands
|
||||||
|
|
||||||
|
class command(sunhpc.commands.Command):
|
||||||
|
MustBeRoot = 0
|
||||||
70
lib/sunhpc/sunhpc/commands/list/help/__init__.py
Normal file
70
lib/sunhpc/sunhpc/commands/list/help/__init__.py
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
import os
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
import sunhpc.file
|
||||||
|
import sunhpc.commands
|
||||||
|
|
||||||
|
class Command(sunhpc.commands.list.command):
|
||||||
|
"""
|
||||||
|
The Help Command print the usage of all the registered commands
|
||||||
|
|
||||||
|
<param optional='1' type='string' name='subdir'>
|
||||||
|
Relative of Python commands for listing help. This is used internally
|
||||||
|
only.
|
||||||
|
</param>
|
||||||
|
|
||||||
|
<example cmd='list help'>
|
||||||
|
List the help of all the commands.
|
||||||
|
</example>
|
||||||
|
|
||||||
|
<example cmd='list help subdir=list/host'>
|
||||||
|
List help for all commands under list/host
|
||||||
|
</example>
|
||||||
|
"""
|
||||||
|
|
||||||
|
def run(self, params, args):
|
||||||
|
(subdir, cols) = self.fillParams([ ('subdir', ),
|
||||||
|
('cols', 80) ], params)
|
||||||
|
|
||||||
|
if subdir:
|
||||||
|
filepath = os.path.join(sunhpc.commands.__path__[0], subdir)
|
||||||
|
modpath = 'sunhpc.commands.%s' % '.'.join(subdir.split('os.sep'))
|
||||||
|
else:
|
||||||
|
filepath = sunhpc.commands.__path__[0]
|
||||||
|
modpath = 'sunhpc.commands'
|
||||||
|
|
||||||
|
tree = sunhpc.file.Tree(filepath)
|
||||||
|
dirs = tree.getDirs()
|
||||||
|
sorted(dirs)
|
||||||
|
|
||||||
|
if 'COLUMNS' in os.environ:
|
||||||
|
cols = os.environ['COLUMNS']
|
||||||
|
|
||||||
|
for dir in dirs:
|
||||||
|
if not dir: continue
|
||||||
|
|
||||||
|
module = '%s.%s' % (modpath, '.'.join(dir.split(os.sep)))
|
||||||
|
__import__(module)
|
||||||
|
module = eval(module)
|
||||||
|
|
||||||
|
try:
|
||||||
|
o = getattr(module, 'Command')(None)
|
||||||
|
except AttributeError:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if o.MustBeRoot and not self.isRootUser():
|
||||||
|
continue
|
||||||
|
|
||||||
|
cmd = ' '.join(dir.split(os.sep))
|
||||||
|
l = len(cmd) + 1
|
||||||
|
s = ''
|
||||||
|
for arg in o.usage().split():
|
||||||
|
if l + len(arg) < cols or cols == 0:
|
||||||
|
s += '%s ' % arg
|
||||||
|
l += len(arg) + 1 # space
|
||||||
|
else:
|
||||||
|
s += '\n\t%s ' % arg
|
||||||
|
l = len(arg) + 9 # tab + space
|
||||||
|
|
||||||
|
self.addText('%s %s\n' % (cmd, s))
|
||||||
|
|
||||||
426
lib/sunhpc/sunhpc/file.py
Normal file
426
lib/sunhpc/sunhpc/file.py
Normal file
@@ -0,0 +1,426 @@
|
|||||||
|
import os
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
import shutil
|
||||||
|
import xml.sax
|
||||||
|
import sunhpc.util
|
||||||
|
|
||||||
|
class File:
|
||||||
|
def __init__(self, file, timestamp=None, size=None):
|
||||||
|
self.setFile(file, timestamp, size)
|
||||||
|
self.imortal = 0
|
||||||
|
|
||||||
|
def __cmp__(self, file):
|
||||||
|
if self.getBaseName() != file.getBaseName() or \
|
||||||
|
self.timestamp == file.timestamp:
|
||||||
|
rc = 0
|
||||||
|
elif self.timestamp > file.timestamp:
|
||||||
|
rc = 1
|
||||||
|
else:
|
||||||
|
rc = -1
|
||||||
|
|
||||||
|
if rc and self.imortal + file.imortal == 1:
|
||||||
|
if self.imortal:
|
||||||
|
rc = 1
|
||||||
|
else:
|
||||||
|
rc = -1
|
||||||
|
|
||||||
|
return rc
|
||||||
|
|
||||||
|
def setFile(self, file, timestamp=None, size=None):
|
||||||
|
self.pathname = os.path.dirname(file)
|
||||||
|
self.filename = os.path.basename(file)
|
||||||
|
|
||||||
|
if None not in (timestamp, size):
|
||||||
|
self.timestamp = timestamp
|
||||||
|
self.size = size
|
||||||
|
elif not os.path.islink(file):
|
||||||
|
self.timestamp = os.path.getmtime(file)
|
||||||
|
self.size = os.path.getsize(file)
|
||||||
|
else:
|
||||||
|
orig = os.readlink(file)
|
||||||
|
if os.path.isfile(orig):
|
||||||
|
self.timestamp = os.path.getmtime(orig)
|
||||||
|
self.size = os.path.getsize(file)
|
||||||
|
else:
|
||||||
|
self.timestamp = 0
|
||||||
|
self.size = 0
|
||||||
|
|
||||||
|
def explode(self):
|
||||||
|
file = self.getFullName()
|
||||||
|
if os.path.islink(file):
|
||||||
|
orig = os.readlink(file)
|
||||||
|
if os.path.isfile(orig):
|
||||||
|
os.unlink(file)
|
||||||
|
shutil.copy2(orig, file)
|
||||||
|
# Fix the timestamp back to that of
|
||||||
|
# the original file. The above copy seems
|
||||||
|
# to do this for us, but I'm going to
|
||||||
|
# leave this in to make sure it always works.
|
||||||
|
tm = os.path.getmtime(orig)
|
||||||
|
os.utime(file, (tm, tm))
|
||||||
|
|
||||||
|
def setImortal(self):
|
||||||
|
self.imortal = 1
|
||||||
|
|
||||||
|
def getTimestamp(self):
|
||||||
|
return self.timestamp
|
||||||
|
|
||||||
|
def getSize(self):
|
||||||
|
return float(self.size) / (1024 * 1024)
|
||||||
|
|
||||||
|
def getUniqueName(self):
|
||||||
|
return self.filename
|
||||||
|
|
||||||
|
def getBaseName(self):
|
||||||
|
return self.filename
|
||||||
|
|
||||||
|
def getName(self):
|
||||||
|
return self.filename
|
||||||
|
|
||||||
|
def getShortName(self):
|
||||||
|
return os.path.splitext(self.filename)[0]
|
||||||
|
|
||||||
|
def getPath(self):
|
||||||
|
return self.pathname
|
||||||
|
|
||||||
|
def getFullName(self):
|
||||||
|
return str(os.path.join(self.pathname, self.filename))
|
||||||
|
|
||||||
|
def symlink(self, target, base=''):
|
||||||
|
if os.path.isfile(target) or os.path.islink(target):
|
||||||
|
os.unlink(target)
|
||||||
|
os.symlink(self.getFullName(), target)
|
||||||
|
|
||||||
|
def chmod(self, mode):
|
||||||
|
if os.path.exists(self.getFullName()):
|
||||||
|
os.chmod(self.getFullName(), mode)
|
||||||
|
|
||||||
|
def dump(self):
|
||||||
|
print('%s(%s)' % (self.filename, self.pathname))
|
||||||
|
|
||||||
|
class RPMBaseFile(File):
|
||||||
|
|
||||||
|
def __init__(self, file, timestamp=None, size=None, ext=1):
|
||||||
|
File.__init__(self, file, timestamp, size)
|
||||||
|
self.result = []
|
||||||
|
# Remove ext count extensions, the defualt is 1, but for
|
||||||
|
# rolls we remove two (.diskN.iso)
|
||||||
|
|
||||||
|
s = self.filename # name-ver-rpmver.arch.rpm
|
||||||
|
for x in range(0, ext):
|
||||||
|
i = s.rfind('.')
|
||||||
|
s = self.filename[:i]
|
||||||
|
|
||||||
|
i = s.rfind('.')
|
||||||
|
self.result.append(s[i+1:]) # get architecture string
|
||||||
|
s = self.filename[:i]
|
||||||
|
|
||||||
|
i = s.rfind('-')
|
||||||
|
self.release = s[i+1:]
|
||||||
|
self.result.append(self.versionList(s[i+1:])) # get release string
|
||||||
|
s = self.filename[:i]
|
||||||
|
|
||||||
|
i = s.rfind('-')
|
||||||
|
self.version = s[i+1:]
|
||||||
|
self.result.append(self.versionList(s[i+1:])) # get version string
|
||||||
|
|
||||||
|
self.result.append(self.filename[:i]) # get package name
|
||||||
|
self.result.reverse() # we built the list backwards
|
||||||
|
|
||||||
|
def versionList(self, s):
|
||||||
|
result = []
|
||||||
|
for e in re.split('\.+|_+', s):
|
||||||
|
num = ''
|
||||||
|
alpha = ''
|
||||||
|
l = []
|
||||||
|
for c in e:
|
||||||
|
if c in string.digits:
|
||||||
|
num = num + c
|
||||||
|
if alpha:
|
||||||
|
l.append(alpha)
|
||||||
|
alpha = ''
|
||||||
|
else:
|
||||||
|
alpha = alpha + c
|
||||||
|
if num:
|
||||||
|
l.append(int(num))
|
||||||
|
num = ''
|
||||||
|
if alpha:
|
||||||
|
l.append(alpha)
|
||||||
|
if num:
|
||||||
|
l.append(int(num))
|
||||||
|
result.append(l)
|
||||||
|
return result
|
||||||
|
|
||||||
|
def getBaseName(self):
|
||||||
|
return self.result[0]
|
||||||
|
|
||||||
|
def getUniqueName(self):
|
||||||
|
return '%s-%s' % (self.result[0], self.result[3])
|
||||||
|
|
||||||
|
class RPMFile(RPMBaseFile):
|
||||||
|
|
||||||
|
def __init__(self, file, timestamp=None, size=None):
|
||||||
|
RPMBaseFile.__init__(self, file, timestamp, size)
|
||||||
|
|
||||||
|
def __cmp__(self, file):
|
||||||
|
if self.getPackageArch() != file.getPackageArch():
|
||||||
|
rc = 0
|
||||||
|
else:
|
||||||
|
# For RPM Files, if the timestamps are within 2 minutes
|
||||||
|
# of each other check
|
||||||
|
# the Buildtime of the RPM
|
||||||
|
if abs(self.timestamp - int(file.timestamp)) < 120:
|
||||||
|
# print "CMP %s:%s" % (self.getFullName(), file.getFullName())
|
||||||
|
f1 = os.popen(f"rpm -qp --qf '%%{BUILDTIME}' %s" % self.getFullName())
|
||||||
|
self.timestamp = float(f1.readline().strip())
|
||||||
|
f1.close()
|
||||||
|
|
||||||
|
f2 = os.popen(f"rpm -qp --qf '%%{BUILDTIME}' %s" % file.getFullName())
|
||||||
|
file.timestamp = float(f2.readline().strip())
|
||||||
|
f2.close()
|
||||||
|
rc = File.__cmp__(self, file)
|
||||||
|
|
||||||
|
return rc
|
||||||
|
|
||||||
|
def getPackageName(self):
|
||||||
|
return self.getBaseName()
|
||||||
|
|
||||||
|
def getPackageVersion(self):
|
||||||
|
return self.result[1]
|
||||||
|
|
||||||
|
def getPackageRelease(self):
|
||||||
|
return self.result[2]
|
||||||
|
|
||||||
|
def getPackageVersionString(self):
|
||||||
|
return self.version
|
||||||
|
|
||||||
|
def getPackageArch(self):
|
||||||
|
return self.result[3]
|
||||||
|
|
||||||
|
def installPackage(self, root, flags=''):
|
||||||
|
'''Installs the RPM at the given root directory. This is
|
||||||
|
used for patching RPMs into the distribution and making
|
||||||
|
bootable CDs'''
|
||||||
|
pass
|
||||||
|
|
||||||
|
dbdir = os.path.join(root, 'var', 'lib', 'rpm')
|
||||||
|
if not os.path.isdir(dbdir):
|
||||||
|
os.makedirs(dbdir)
|
||||||
|
|
||||||
|
cmd = 'rpm -i --nomd5 --force --nodeps --ignorearch '
|
||||||
|
cmd += '--dbpath %s %s ' % (dbdir, flags)
|
||||||
|
cmd += '--badreloc --relocate /=%s %s' % (root, self.getFullName())
|
||||||
|
|
||||||
|
print ('cmd', cmd)
|
||||||
|
retval = os.system(cmd)
|
||||||
|
|
||||||
|
while dbdir:
|
||||||
|
if not os.listdir(dbdir):
|
||||||
|
shutil.rmtree(dbdir)
|
||||||
|
result = dbdir.split(os.sep)
|
||||||
|
dbdir = os.sep.join(result[:-1])
|
||||||
|
|
||||||
|
print ('retval', retval)
|
||||||
|
return retval
|
||||||
|
|
||||||
|
class RollFile(RPMBaseFile):
|
||||||
|
|
||||||
|
def __init__(self, file, timestamp=None, size=None):
|
||||||
|
RPMBaseFile.__init__(self, file, timestamp, size, 2)
|
||||||
|
self.diskID = int(file.split('.')[-2][4:])
|
||||||
|
|
||||||
|
def __cmp__(self, file):
|
||||||
|
if self.getRollArch() != file.getRollArch():
|
||||||
|
rc = 0
|
||||||
|
else:
|
||||||
|
rc = File.__cmp__(self, file)
|
||||||
|
return rc
|
||||||
|
|
||||||
|
def getRollDiskID(self):
|
||||||
|
return self.diskID
|
||||||
|
|
||||||
|
def getRollName(self):
|
||||||
|
return self.getBaseName()
|
||||||
|
|
||||||
|
def getRollVersion(self):
|
||||||
|
return self.result[1]
|
||||||
|
|
||||||
|
def getRollVersionString(self):
|
||||||
|
return self.version
|
||||||
|
|
||||||
|
def getRollReleaseString(self):
|
||||||
|
return self.release
|
||||||
|
|
||||||
|
def getRollArch(self):
|
||||||
|
return self.result[3]
|
||||||
|
|
||||||
|
class RollInfoFile(File,
|
||||||
|
xml.sax.handler.ContentHandler, xml.sax.handler.DTDHandler,
|
||||||
|
xml.sax.handler.EntityResolver, xml.sax.handler.ErrorHandler):
|
||||||
|
|
||||||
|
def __init__(self, file):
|
||||||
|
File.__init__(self, file)
|
||||||
|
|
||||||
|
self.attrs = {}
|
||||||
|
parser = xml.sax.make_parser()
|
||||||
|
parser.setContentHandler(self)
|
||||||
|
fin = open(file, 'r')
|
||||||
|
parser.parse(fin)
|
||||||
|
fin.close()
|
||||||
|
|
||||||
|
def startElement(self, name, attrs):
|
||||||
|
self.attrs[str(name)] = {}
|
||||||
|
for (attrName, attrVal) in attrs.items():
|
||||||
|
self.attrs[str(name)][str(attrName)] = str(attrVal)
|
||||||
|
|
||||||
|
def getXML(self):
|
||||||
|
'''Regenerate the XML file based on what was read in and
|
||||||
|
the current state.'''
|
||||||
|
|
||||||
|
xml = []
|
||||||
|
xml.append('<roll name="%s" interface="%s">' %
|
||||||
|
(self.getRollName(), self.getRollInterface()))
|
||||||
|
|
||||||
|
for tag in self.attrs.keys():
|
||||||
|
if tag == 'roll':
|
||||||
|
continue
|
||||||
|
attrs = ''
|
||||||
|
for key,val in self.attrs[tag].items():
|
||||||
|
attrs += ' %s="%s"' % (key, val)
|
||||||
|
xml.append('\t<%s%s/>' % (tag, attrs))
|
||||||
|
xml.append('</roll>')
|
||||||
|
|
||||||
|
return '\n'.join(xml)
|
||||||
|
|
||||||
|
def getRollName(self):
|
||||||
|
return self.attrs['roll']['name']
|
||||||
|
|
||||||
|
def getRollInterface(self):
|
||||||
|
return self.attrs['roll']['interface']
|
||||||
|
|
||||||
|
def getRollVersion(self):
|
||||||
|
return self.attrs['info']['version']
|
||||||
|
|
||||||
|
def getRollReleaseString(self):
|
||||||
|
return self.attrs['info']['release']
|
||||||
|
|
||||||
|
def getRollOS(self):
|
||||||
|
try:
|
||||||
|
return self.attrs['info']['os']
|
||||||
|
except:
|
||||||
|
return 'linux'
|
||||||
|
|
||||||
|
def setRollArch(self, arch):
|
||||||
|
self.attrs['info']['arch'] = arch
|
||||||
|
|
||||||
|
def getRollArch(self):
|
||||||
|
return self.attrs['info']['arch']
|
||||||
|
|
||||||
|
def getISOMaxSize(self):
|
||||||
|
return float(self.attrs['iso']['maxsize'])
|
||||||
|
|
||||||
|
def setISOMaxSize(self, maxsize):
|
||||||
|
self.attrs['iso']['maxsize'] = maxsize
|
||||||
|
|
||||||
|
def getISOFlags(self):
|
||||||
|
return self.attrs['iso']['mkisofs']
|
||||||
|
|
||||||
|
def getRollRolls(self):
|
||||||
|
return self.attrs['rpm']['rolls']
|
||||||
|
|
||||||
|
def isBootable(self):
|
||||||
|
return int(self.attrs['iso']['bootable'])
|
||||||
|
|
||||||
|
def hasRolls(self):
|
||||||
|
if self.attrs['rpm']['rolls'] != '0':
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def hasRPMS(self):
|
||||||
|
return int(self.attrs['rpm']['bin'])
|
||||||
|
|
||||||
|
def hasSRPMS(self):
|
||||||
|
return int(self.attrs['rpm']['src'])
|
||||||
|
|
||||||
|
class Tree:
|
||||||
|
|
||||||
|
def __init__(self, root):
|
||||||
|
self.root = root
|
||||||
|
self.tree = {}
|
||||||
|
self.build('')
|
||||||
|
|
||||||
|
def getRoot(self):
|
||||||
|
return self.root
|
||||||
|
|
||||||
|
def getDirs(self):
|
||||||
|
return self.tree.keys()
|
||||||
|
|
||||||
|
def clear(self, path=''):
|
||||||
|
l1 = path.split(os.sep)
|
||||||
|
for key in self.tree.keys():
|
||||||
|
l2 = key.split(os.sep)
|
||||||
|
if sunhpc.util.list_isprefix(l1, l2):
|
||||||
|
del self.tree[key]
|
||||||
|
|
||||||
|
def getFiles(self, path=''):
|
||||||
|
try:
|
||||||
|
result = self.tree[path]
|
||||||
|
except KeyError:
|
||||||
|
result = []
|
||||||
|
return result
|
||||||
|
|
||||||
|
def setFiles(self, path, files):
|
||||||
|
self.tree[path] = files
|
||||||
|
|
||||||
|
def build(self, dirs):
|
||||||
|
path = os.path.join(self.root, dirs)
|
||||||
|
if not os.path.isdir(path):
|
||||||
|
return
|
||||||
|
|
||||||
|
# Handle the case where we don't have permission to traverse
|
||||||
|
# into a tree by pruning off the protected sub-tree.
|
||||||
|
try:
|
||||||
|
files = os.listdir(path)
|
||||||
|
except:
|
||||||
|
files = []
|
||||||
|
|
||||||
|
v = []
|
||||||
|
for f in files:
|
||||||
|
filepath = os.path.join(path, f)
|
||||||
|
if os.path.isdir(filepath) and not \
|
||||||
|
os.path.islink(filepath):
|
||||||
|
self.build(os.path.join(dirs, f))
|
||||||
|
else:
|
||||||
|
if re.match('.*\.rpm$', f) != None:
|
||||||
|
v.append(RPMFile(filepath))
|
||||||
|
elif re.match('roll-.*\.iso$', f) != None:
|
||||||
|
v.append(RollFile(filepath))
|
||||||
|
else:
|
||||||
|
v.append(File(filepath))
|
||||||
|
self.tree[dirs] = v
|
||||||
|
|
||||||
|
def dumpDirNames(self):
|
||||||
|
for key in self.tree.keys():
|
||||||
|
print(key)
|
||||||
|
|
||||||
|
def dump(self):
|
||||||
|
self.apply(self.__dumpIter__)
|
||||||
|
|
||||||
|
def apply(self, func, root=None):
|
||||||
|
for key in self.tree.keys():
|
||||||
|
for e in self.tree[key]:
|
||||||
|
func(key, e, root)
|
||||||
|
|
||||||
|
def getSize(self):
|
||||||
|
'Return the size the if Tree in Mbytes'
|
||||||
|
len = 0
|
||||||
|
for key in self.tree.keys():
|
||||||
|
for file in self.tree[key]:
|
||||||
|
len = len + file.getSize()
|
||||||
|
return float(len)
|
||||||
|
|
||||||
|
def __dumpIter__(self, p, f, root):
|
||||||
|
print(p, end='')
|
||||||
|
f.dump()
|
||||||
98
lib/sunhpc/sunhpc/main.py
Normal file
98
lib/sunhpc/sunhpc/main.py
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import tty
|
||||||
|
import yaml
|
||||||
|
import sunhpc
|
||||||
|
import signal
|
||||||
|
import syslog
|
||||||
|
import sunhpc.util
|
||||||
|
|
||||||
|
def _main(argv=None):
|
||||||
|
|
||||||
|
try:
|
||||||
|
database = None
|
||||||
|
except ImportError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
if len(argv) == 1:
|
||||||
|
args = ['list', 'help']
|
||||||
|
else:
|
||||||
|
args = sys.argv[1:]
|
||||||
|
|
||||||
|
module = None
|
||||||
|
cmd = args[0].split()
|
||||||
|
if len(cmd) > 1:
|
||||||
|
s = 'sunhpc.commands.%s' % '.'.join(cmd)
|
||||||
|
try:
|
||||||
|
__import__(s)
|
||||||
|
module = eval(s)
|
||||||
|
i = 1
|
||||||
|
except:
|
||||||
|
module = None
|
||||||
|
|
||||||
|
if not module:
|
||||||
|
for i in range(len(args), 0, -1):
|
||||||
|
s = 'sunhpc.commands.%s' % '.'.join(args[:i])
|
||||||
|
try:
|
||||||
|
__import__(s)
|
||||||
|
module = eval(s)
|
||||||
|
if module:
|
||||||
|
break
|
||||||
|
except ImportError:
|
||||||
|
#raise
|
||||||
|
continue
|
||||||
|
|
||||||
|
if not module:
|
||||||
|
print ('error - invalid sunhpc command "%s"' % args[0])
|
||||||
|
sys.exit(-1)
|
||||||
|
|
||||||
|
name = ' '.join('.'.split(s))
|
||||||
|
|
||||||
|
try:
|
||||||
|
command = getattr(module, 'Command')(database)
|
||||||
|
except AttributeError:
|
||||||
|
import sunhpc.commands.list.help
|
||||||
|
help = sunhpc.commands.list.help.Command(database)
|
||||||
|
fullmodpath = s.split('.')
|
||||||
|
submodpath = '/'.join(fullmodpath[2:])
|
||||||
|
help.run({'subdir': submodpath}, [])
|
||||||
|
print (help.getText())
|
||||||
|
sys.exit(-1)
|
||||||
|
|
||||||
|
if command.MustBeRoot and not (
|
||||||
|
command.isRootUser() or command.isApacheUser()):
|
||||||
|
os.system('sudo %s' % ' '.join(sys.argv))
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
command.runWrapper(name, args[i:])
|
||||||
|
text = command.getText()
|
||||||
|
if len(text) > 0:
|
||||||
|
print (text, end=' ')
|
||||||
|
if text[len(text)-1] != '\n':
|
||||||
|
print ()
|
||||||
|
except sunhpc.util.CommandError as e:
|
||||||
|
print ('Error: ', e)
|
||||||
|
print (command.usage())
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
syslog.closelog()
|
||||||
|
|
||||||
|
|
||||||
|
def main(argv=None):
|
||||||
|
|
||||||
|
_main(argv)
|
||||||
|
'''
|
||||||
|
try:
|
||||||
|
return _main(argv)
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
sys.stderr.write("\n")
|
||||||
|
tty.error("Keyboard interrupt.")
|
||||||
|
return signal.SIGINT.value
|
||||||
|
|
||||||
|
except SystemExit as e:
|
||||||
|
return e.code
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
tty.error(e)
|
||||||
|
return 3
|
||||||
|
'''
|
||||||
147
lib/sunhpc/sunhpc/paths.py
Normal file
147
lib/sunhpc/sunhpc/paths.py
Normal file
@@ -0,0 +1,147 @@
|
|||||||
|
# Copyright Sunhpc Project Developers. See COPYRIGHT file for details.
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
|
||||||
|
|
||||||
|
"""Defines paths that are part of Sunhpc's directory structure.
|
||||||
|
|
||||||
|
Do not import other ``sunhpc`` modules here. This module is used
|
||||||
|
throughout Sunhpc and should bring in a minimal number of external
|
||||||
|
dependencies.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
from pathlib import PurePath, Path
|
||||||
|
|
||||||
|
#: This file lives in /opt/sunhpc/lib/sunhpc/sunhpc/paths.py
|
||||||
|
#: prefix: /opt/sunhpc
|
||||||
|
prefix = str(Path(__file__).parent.parent.parent.parent)
|
||||||
|
|
||||||
|
#: synonym for prefix
|
||||||
|
sunhpc_root = prefix
|
||||||
|
|
||||||
|
#: bin directory in the sunhpc prefix
|
||||||
|
bin_path = os.path.join(prefix, "bin")
|
||||||
|
|
||||||
|
#: The sunhpc script itself
|
||||||
|
sunhpc_script = os.path.join(bin_path, "sunhpc")
|
||||||
|
|
||||||
|
#: The sbang script in the sunhpc installation
|
||||||
|
sbang_script = os.path.join(bin_path, "sbang")
|
||||||
|
|
||||||
|
# sunhpc directory hierarchy
|
||||||
|
lib_path = os.path.join(prefix, "lib", "sunhpc")
|
||||||
|
module_path = os.path.join(lib_path, "sunhpc")
|
||||||
|
vendor_path = os.path.join(module_path, "vendor")
|
||||||
|
command_path = os.path.join(module_path, "cmd")
|
||||||
|
analyzers_path = os.path.join(module_path, "analyzers")
|
||||||
|
platform_path = os.path.join(module_path, "platforms")
|
||||||
|
compilers_path = os.path.join(module_path, "compilers")
|
||||||
|
operating_system_path = os.path.join(module_path, "operating_systems")
|
||||||
|
test_path = os.path.join(module_path, "test")
|
||||||
|
hooks_path = os.path.join(module_path, "hooks")
|
||||||
|
opt_path = os.path.join(prefix, "opt")
|
||||||
|
share_path = os.path.join(prefix, "share", "sunhpc")
|
||||||
|
etc_path = os.path.join(prefix, "etc", "sunhpc")
|
||||||
|
|
||||||
|
#
|
||||||
|
# Things in $sunhpc/etc/sunhpc
|
||||||
|
#
|
||||||
|
default_license_dir = os.path.join(etc_path, "licenses")
|
||||||
|
|
||||||
|
#
|
||||||
|
# Things in $sunhpc/var/sunhpc
|
||||||
|
#
|
||||||
|
var_path = os.path.join(prefix, "var", "sunhpc")
|
||||||
|
|
||||||
|
# read-only things in $sunhpc/var/sunhpc
|
||||||
|
repos_path = os.path.join(var_path, "repos")
|
||||||
|
test_repos_path = os.path.join(var_path, "test_repos")
|
||||||
|
mock_packages_path = os.path.join(test_repos_path, "sunhpc_repo", "builtin_mock")
|
||||||
|
|
||||||
|
#
|
||||||
|
# Writable things in $sunhpc/var/sunhpc
|
||||||
|
# TODO: Deprecate these, as we want a read-only sunhpc prefix by default.
|
||||||
|
# TODO: These should probably move to user cache, or some other location.
|
||||||
|
#
|
||||||
|
# fetch cache for downloaded files
|
||||||
|
default_fetch_cache_path = os.path.join(var_path, "cache")
|
||||||
|
|
||||||
|
# GPG paths.
|
||||||
|
gpg_keys_path = os.path.join(var_path, "gpg")
|
||||||
|
mock_gpg_data_path = os.path.join(var_path, "gpg.mock", "data")
|
||||||
|
mock_gpg_keys_path = os.path.join(var_path, "gpg.mock", "keys")
|
||||||
|
gpg_path = os.path.join(opt_path, "sunhpc", "gpg")
|
||||||
|
|
||||||
|
|
||||||
|
# Below paths are where Sunhpc can write information for the user.
|
||||||
|
# Some are caches, some are not exactly caches.
|
||||||
|
#
|
||||||
|
# The options that start with `default_` below are overridable in
|
||||||
|
# `config.yaml`, but they default to use `user_cache_path/<location>`.
|
||||||
|
#
|
||||||
|
# You can override the top-level directory (the user cache path) by
|
||||||
|
# setting `SUNHPC_USER_CACHE_PATH`. Otherwise it defaults to ~/.sunhpc.
|
||||||
|
#
|
||||||
|
def _get_user_cache_path():
|
||||||
|
return os.path.expanduser(os.getenv("SUNHPC_USER_CACHE_PATH") or "~%s.sunhpc" % os.sep)
|
||||||
|
|
||||||
|
|
||||||
|
user_cache_path = str(PurePath(_get_user_cache_path()))
|
||||||
|
|
||||||
|
#: junit, cdash, etc. reports about builds
|
||||||
|
reports_path = os.path.join(user_cache_path, "reports")
|
||||||
|
|
||||||
|
#: installation test (sunhpc test) output
|
||||||
|
default_test_path = os.path.join(user_cache_path, "test")
|
||||||
|
|
||||||
|
#: sunhpc monitor analysis directories
|
||||||
|
default_monitor_path = os.path.join(reports_path, "monitor")
|
||||||
|
|
||||||
|
#: git repositories fetched to compare commits to versions
|
||||||
|
user_repos_cache_path = os.path.join(user_cache_path, "git_repos")
|
||||||
|
|
||||||
|
#: default location where remote package repositories are cloned
|
||||||
|
package_repos_path = os.path.join(user_cache_path, "package_repos")
|
||||||
|
|
||||||
|
#: bootstrap store for bootstrapping clingo and other tools
|
||||||
|
default_user_bootstrap_path = os.path.join(user_cache_path, "bootstrap")
|
||||||
|
|
||||||
|
# Below paths pull configuration from the host environment.
|
||||||
|
#
|
||||||
|
# There are three environment variables you can use to isolate sunhpc from
|
||||||
|
# the host environment:
|
||||||
|
# - `SUNHPC_USER_CONFIG_PATH`: override `~/.sunhpc` location (for config and caches)
|
||||||
|
# - `SUNHPC_SYSTEM_CONFIG_PATH`: override `/etc/sunhpc` configuration scope.
|
||||||
|
# - `SUNHPC_DISABLE_LOCAL_CONFIG`: disable both of these locations.
|
||||||
|
|
||||||
|
|
||||||
|
# User configuration and caches in $HOME/.sunhpc
|
||||||
|
def _get_user_config_path():
|
||||||
|
return os.path.expanduser(os.getenv("SUNHPC_USER_CONFIG_PATH") or "~%s.sunhpc" % os.sep)
|
||||||
|
|
||||||
|
|
||||||
|
# Configuration in /etc/sunhpc on the system
|
||||||
|
def _get_system_config_path():
|
||||||
|
return os.path.expanduser(
|
||||||
|
os.getenv("SUNHPC_SYSTEM_CONFIG_PATH") or os.sep + os.path.join("etc", "sunhpc")
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
#: User configuration location
|
||||||
|
user_config_path = _get_user_config_path()
|
||||||
|
|
||||||
|
#: System configuration location
|
||||||
|
system_config_path = _get_system_config_path()
|
||||||
|
|
||||||
|
#: Recorded directory where sunhpc command was originally invoked
|
||||||
|
sunhpc_working_dir = None
|
||||||
|
|
||||||
|
|
||||||
|
def set_working_dir():
|
||||||
|
"""Change the working directory to getcwd, or sunhpc prefix if no cwd."""
|
||||||
|
global sunhpc_working_dir
|
||||||
|
try:
|
||||||
|
sunhpc_working_dir = os.getcwd()
|
||||||
|
except OSError:
|
||||||
|
os.chdir(prefix)
|
||||||
|
sunhpc_working_dir = prefix
|
||||||
183
lib/sunhpc/sunhpc/util.py
Normal file
183
lib/sunhpc/sunhpc/util.py
Normal file
@@ -0,0 +1,183 @@
|
|||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import xml.sax
|
||||||
|
import subprocess
|
||||||
|
from xml.sax import handler
|
||||||
|
|
||||||
|
class SunhpcException(Exception):
|
||||||
|
'Base class for Sunhpc exceptions.'
|
||||||
|
pass
|
||||||
|
|
||||||
|
class HostnotfoundException(SunhpcException):
|
||||||
|
'Host not found exception.'
|
||||||
|
pass
|
||||||
|
|
||||||
|
class ParameterNotValid(SunhpcException):
|
||||||
|
'Parameter not valid exception.'
|
||||||
|
pass
|
||||||
|
|
||||||
|
class CommandError(SunhpcException):
|
||||||
|
'Command error exception.'
|
||||||
|
pass
|
||||||
|
|
||||||
|
# An exception for Kickstart builder trinity: kcgi, kgen, kp perrors.
|
||||||
|
class KickstartError(SunhpcException):
|
||||||
|
pass
|
||||||
|
class KickstartGraphError(KickstartError):
|
||||||
|
pass
|
||||||
|
class KickstartNodeError(KickstartError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class Struct:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def escapeAttr(value):
|
||||||
|
if value is None:
|
||||||
|
return value
|
||||||
|
return xml.sax.saxutils.escape(value, {
|
||||||
|
"\"": """,
|
||||||
|
"%": "%i;",
|
||||||
|
"'": "\\'"
|
||||||
|
})
|
||||||
|
|
||||||
|
def unescapeAttr(value):
|
||||||
|
if value is None:
|
||||||
|
return value
|
||||||
|
return xml.sax.saxutils.unescape(value, {
|
||||||
|
""": "\"",
|
||||||
|
"%": "%"
|
||||||
|
})
|
||||||
|
|
||||||
|
def escapeStringForShell(value):
|
||||||
|
return value.replace('\"', '\\\"')
|
||||||
|
|
||||||
|
def str2bool(s):
|
||||||
|
if s and s.upper() in [ 'ON', 'YES', 'Y', 'TRUE', '1', 'ENABLE', 'ENABLED']:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def bool2str(b):
|
||||||
|
if b:
|
||||||
|
return 'yes'
|
||||||
|
else:
|
||||||
|
return 'no'
|
||||||
|
|
||||||
|
def list2str(l):
|
||||||
|
s = ''
|
||||||
|
for e in l:
|
||||||
|
s = s + e
|
||||||
|
return s
|
||||||
|
|
||||||
|
def listcmp(l1, l2):
|
||||||
|
return map(lambda x, y: x == y, l1, l2)
|
||||||
|
|
||||||
|
def listdup(e, n):
|
||||||
|
l = []
|
||||||
|
for i in range(0, n):
|
||||||
|
l.append(e)
|
||||||
|
return l
|
||||||
|
|
||||||
|
def list_isprefix(l1, l2):
|
||||||
|
l = listcmp(l1, l2)
|
||||||
|
if all(l):
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
def list_issuffix(l1, l2):
|
||||||
|
l = listcmp(l1, l2)
|
||||||
|
for i in range(0, len(l)):
|
||||||
|
if not l[i]:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def getNativeArch():
|
||||||
|
arch = os.uname()[4]
|
||||||
|
if arch in [ 'i386', 'i686']:
|
||||||
|
return 'i386'
|
||||||
|
return arch
|
||||||
|
|
||||||
|
def mkdir(newdir):
|
||||||
|
if os.path.isdir(newdir):
|
||||||
|
pass
|
||||||
|
elif os.path.isfile(newdir):
|
||||||
|
raise OSError("a file with the same name as the desired dir '%s', already exists." % newdir)
|
||||||
|
else:
|
||||||
|
head, tail = os.path.split(newdir)
|
||||||
|
if head and not os.path.isdir(head):
|
||||||
|
mkdir(head)
|
||||||
|
if tail:
|
||||||
|
os.mkdir(newdir)
|
||||||
|
|
||||||
|
class ParseXML(
|
||||||
|
handler.ContentHandler, handler.DTDHandler,
|
||||||
|
handler.EntityResolver, handler.ErrorHandler):
|
||||||
|
|
||||||
|
'''A helper class to for XML parsers. Uses our
|
||||||
|
startElement_name style.
|
||||||
|
'''
|
||||||
|
def __init__(self, app=None):
|
||||||
|
handler.ContentHandler.__init__(self)
|
||||||
|
self.app = app
|
||||||
|
self.text = ''
|
||||||
|
|
||||||
|
def startElement(self, name, attrs):
|
||||||
|
try:
|
||||||
|
f = getattr(self, "startElement_" + name)
|
||||||
|
f(name, attrs)
|
||||||
|
except AttributeError:
|
||||||
|
return
|
||||||
|
|
||||||
|
def endElement(self, name):
|
||||||
|
try:
|
||||||
|
f = getattr(self, "endElement_" + name)
|
||||||
|
f(name)
|
||||||
|
except AttributeError:
|
||||||
|
return
|
||||||
|
|
||||||
|
def characters(self, content: str) -> None:
|
||||||
|
self.text += content
|
||||||
|
|
||||||
|
def system(cmd, type='standard'):
|
||||||
|
if type == 'spinner':
|
||||||
|
return startSpinner(cmd)
|
||||||
|
else:
|
||||||
|
return subprocess.call(cmd, shell=True)
|
||||||
|
|
||||||
|
def startSpinner(cmd):
|
||||||
|
|
||||||
|
p = subprocess.Popen(cmd, shell=True,
|
||||||
|
stdin=subprocess.PIPE,
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
stderr=subprocess.PIPE,
|
||||||
|
close_fds=True)
|
||||||
|
w, r, e = (p.stdin, p.stdout, p.stderr)
|
||||||
|
currLength = 0
|
||||||
|
prevLength = 0
|
||||||
|
spinChars = '|/-\\'
|
||||||
|
spinIndex = 0
|
||||||
|
while True:
|
||||||
|
line = e.readline()
|
||||||
|
if not line:
|
||||||
|
break
|
||||||
|
if isinstance(line, bytes):
|
||||||
|
line = line.decode('utf-8', errors='replace')
|
||||||
|
if len(line) > 79:
|
||||||
|
data = line[0:78]
|
||||||
|
else:
|
||||||
|
data = line[:-1]
|
||||||
|
currLength = len(data)
|
||||||
|
pad = ''
|
||||||
|
for i in range(0, prevLength - currLength):
|
||||||
|
pad = pad + ' '
|
||||||
|
spin = spinChars[spinIndex % len(spinChars)]
|
||||||
|
spinIndex = spinIndex + 1
|
||||||
|
print (spin + data + pad + '\r', end='', flush=True)
|
||||||
|
prevLength = currLength
|
||||||
|
w.close()
|
||||||
|
r.close()
|
||||||
|
e.close()
|
||||||
|
pad = ''
|
||||||
|
for i in range(0, 78):
|
||||||
|
pad = pad + ' '
|
||||||
|
print ('\r%s\r' % pad, end='', flush=True)
|
||||||
390
lib/sunhpc/yaml/__init__.py
Normal file
390
lib/sunhpc/yaml/__init__.py
Normal file
@@ -0,0 +1,390 @@
|
|||||||
|
|
||||||
|
from .error import *
|
||||||
|
|
||||||
|
from .tokens import *
|
||||||
|
from .events import *
|
||||||
|
from .nodes import *
|
||||||
|
|
||||||
|
from .loader import *
|
||||||
|
from .dumper import *
|
||||||
|
|
||||||
|
__version__ = '6.0.3'
|
||||||
|
try:
|
||||||
|
from .cyaml import *
|
||||||
|
__with_libyaml__ = True
|
||||||
|
except ImportError:
|
||||||
|
__with_libyaml__ = False
|
||||||
|
|
||||||
|
import io
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# XXX "Warnings control" is now deprecated. Leaving in the API function to not
|
||||||
|
# break code that uses it.
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def warnings(settings=None):
|
||||||
|
if settings is None:
|
||||||
|
return {}
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def scan(stream, Loader=Loader):
|
||||||
|
"""
|
||||||
|
Scan a YAML stream and produce scanning tokens.
|
||||||
|
"""
|
||||||
|
loader = Loader(stream)
|
||||||
|
try:
|
||||||
|
while loader.check_token():
|
||||||
|
yield loader.get_token()
|
||||||
|
finally:
|
||||||
|
loader.dispose()
|
||||||
|
|
||||||
|
def parse(stream, Loader=Loader):
|
||||||
|
"""
|
||||||
|
Parse a YAML stream and produce parsing events.
|
||||||
|
"""
|
||||||
|
loader = Loader(stream)
|
||||||
|
try:
|
||||||
|
while loader.check_event():
|
||||||
|
yield loader.get_event()
|
||||||
|
finally:
|
||||||
|
loader.dispose()
|
||||||
|
|
||||||
|
def compose(stream, Loader=Loader):
|
||||||
|
"""
|
||||||
|
Parse the first YAML document in a stream
|
||||||
|
and produce the corresponding representation tree.
|
||||||
|
"""
|
||||||
|
loader = Loader(stream)
|
||||||
|
try:
|
||||||
|
return loader.get_single_node()
|
||||||
|
finally:
|
||||||
|
loader.dispose()
|
||||||
|
|
||||||
|
def compose_all(stream, Loader=Loader):
|
||||||
|
"""
|
||||||
|
Parse all YAML documents in a stream
|
||||||
|
and produce corresponding representation trees.
|
||||||
|
"""
|
||||||
|
loader = Loader(stream)
|
||||||
|
try:
|
||||||
|
while loader.check_node():
|
||||||
|
yield loader.get_node()
|
||||||
|
finally:
|
||||||
|
loader.dispose()
|
||||||
|
|
||||||
|
def load(stream, Loader):
|
||||||
|
"""
|
||||||
|
Parse the first YAML document in a stream
|
||||||
|
and produce the corresponding Python object.
|
||||||
|
"""
|
||||||
|
loader = Loader(stream)
|
||||||
|
try:
|
||||||
|
return loader.get_single_data()
|
||||||
|
finally:
|
||||||
|
loader.dispose()
|
||||||
|
|
||||||
|
def load_all(stream, Loader):
|
||||||
|
"""
|
||||||
|
Parse all YAML documents in a stream
|
||||||
|
and produce corresponding Python objects.
|
||||||
|
"""
|
||||||
|
loader = Loader(stream)
|
||||||
|
try:
|
||||||
|
while loader.check_data():
|
||||||
|
yield loader.get_data()
|
||||||
|
finally:
|
||||||
|
loader.dispose()
|
||||||
|
|
||||||
|
def full_load(stream):
|
||||||
|
"""
|
||||||
|
Parse the first YAML document in a stream
|
||||||
|
and produce the corresponding Python object.
|
||||||
|
|
||||||
|
Resolve all tags except those known to be
|
||||||
|
unsafe on untrusted input.
|
||||||
|
"""
|
||||||
|
return load(stream, FullLoader)
|
||||||
|
|
||||||
|
def full_load_all(stream):
|
||||||
|
"""
|
||||||
|
Parse all YAML documents in a stream
|
||||||
|
and produce corresponding Python objects.
|
||||||
|
|
||||||
|
Resolve all tags except those known to be
|
||||||
|
unsafe on untrusted input.
|
||||||
|
"""
|
||||||
|
return load_all(stream, FullLoader)
|
||||||
|
|
||||||
|
def safe_load(stream):
|
||||||
|
"""
|
||||||
|
Parse the first YAML document in a stream
|
||||||
|
and produce the corresponding Python object.
|
||||||
|
|
||||||
|
Resolve only basic YAML tags. This is known
|
||||||
|
to be safe for untrusted input.
|
||||||
|
"""
|
||||||
|
return load(stream, SafeLoader)
|
||||||
|
|
||||||
|
def safe_load_all(stream):
|
||||||
|
"""
|
||||||
|
Parse all YAML documents in a stream
|
||||||
|
and produce corresponding Python objects.
|
||||||
|
|
||||||
|
Resolve only basic YAML tags. This is known
|
||||||
|
to be safe for untrusted input.
|
||||||
|
"""
|
||||||
|
return load_all(stream, SafeLoader)
|
||||||
|
|
||||||
|
def unsafe_load(stream):
|
||||||
|
"""
|
||||||
|
Parse the first YAML document in a stream
|
||||||
|
and produce the corresponding Python object.
|
||||||
|
|
||||||
|
Resolve all tags, even those known to be
|
||||||
|
unsafe on untrusted input.
|
||||||
|
"""
|
||||||
|
return load(stream, UnsafeLoader)
|
||||||
|
|
||||||
|
def unsafe_load_all(stream):
|
||||||
|
"""
|
||||||
|
Parse all YAML documents in a stream
|
||||||
|
and produce corresponding Python objects.
|
||||||
|
|
||||||
|
Resolve all tags, even those known to be
|
||||||
|
unsafe on untrusted input.
|
||||||
|
"""
|
||||||
|
return load_all(stream, UnsafeLoader)
|
||||||
|
|
||||||
|
def emit(events, stream=None, Dumper=Dumper,
|
||||||
|
canonical=None, indent=None, width=None,
|
||||||
|
allow_unicode=None, line_break=None):
|
||||||
|
"""
|
||||||
|
Emit YAML parsing events into a stream.
|
||||||
|
If stream is None, return the produced string instead.
|
||||||
|
"""
|
||||||
|
getvalue = None
|
||||||
|
if stream is None:
|
||||||
|
stream = io.StringIO()
|
||||||
|
getvalue = stream.getvalue
|
||||||
|
dumper = Dumper(stream, canonical=canonical, indent=indent, width=width,
|
||||||
|
allow_unicode=allow_unicode, line_break=line_break)
|
||||||
|
try:
|
||||||
|
for event in events:
|
||||||
|
dumper.emit(event)
|
||||||
|
finally:
|
||||||
|
dumper.dispose()
|
||||||
|
if getvalue:
|
||||||
|
return getvalue()
|
||||||
|
|
||||||
|
def serialize_all(nodes, stream=None, Dumper=Dumper,
|
||||||
|
canonical=None, indent=None, width=None,
|
||||||
|
allow_unicode=None, line_break=None,
|
||||||
|
encoding=None, explicit_start=None, explicit_end=None,
|
||||||
|
version=None, tags=None):
|
||||||
|
"""
|
||||||
|
Serialize a sequence of representation trees into a YAML stream.
|
||||||
|
If stream is None, return the produced string instead.
|
||||||
|
"""
|
||||||
|
getvalue = None
|
||||||
|
if stream is None:
|
||||||
|
if encoding is None:
|
||||||
|
stream = io.StringIO()
|
||||||
|
else:
|
||||||
|
stream = io.BytesIO()
|
||||||
|
getvalue = stream.getvalue
|
||||||
|
dumper = Dumper(stream, canonical=canonical, indent=indent, width=width,
|
||||||
|
allow_unicode=allow_unicode, line_break=line_break,
|
||||||
|
encoding=encoding, version=version, tags=tags,
|
||||||
|
explicit_start=explicit_start, explicit_end=explicit_end)
|
||||||
|
try:
|
||||||
|
dumper.open()
|
||||||
|
for node in nodes:
|
||||||
|
dumper.serialize(node)
|
||||||
|
dumper.close()
|
||||||
|
finally:
|
||||||
|
dumper.dispose()
|
||||||
|
if getvalue:
|
||||||
|
return getvalue()
|
||||||
|
|
||||||
|
def serialize(node, stream=None, Dumper=Dumper, **kwds):
|
||||||
|
"""
|
||||||
|
Serialize a representation tree into a YAML stream.
|
||||||
|
If stream is None, return the produced string instead.
|
||||||
|
"""
|
||||||
|
return serialize_all([node], stream, Dumper=Dumper, **kwds)
|
||||||
|
|
||||||
|
def dump_all(documents, stream=None, Dumper=Dumper,
|
||||||
|
default_style=None, default_flow_style=False,
|
||||||
|
canonical=None, indent=None, width=None,
|
||||||
|
allow_unicode=None, line_break=None,
|
||||||
|
encoding=None, explicit_start=None, explicit_end=None,
|
||||||
|
version=None, tags=None, sort_keys=True):
|
||||||
|
"""
|
||||||
|
Serialize a sequence of Python objects into a YAML stream.
|
||||||
|
If stream is None, return the produced string instead.
|
||||||
|
"""
|
||||||
|
getvalue = None
|
||||||
|
if stream is None:
|
||||||
|
if encoding is None:
|
||||||
|
stream = io.StringIO()
|
||||||
|
else:
|
||||||
|
stream = io.BytesIO()
|
||||||
|
getvalue = stream.getvalue
|
||||||
|
dumper = Dumper(stream, default_style=default_style,
|
||||||
|
default_flow_style=default_flow_style,
|
||||||
|
canonical=canonical, indent=indent, width=width,
|
||||||
|
allow_unicode=allow_unicode, line_break=line_break,
|
||||||
|
encoding=encoding, version=version, tags=tags,
|
||||||
|
explicit_start=explicit_start, explicit_end=explicit_end, sort_keys=sort_keys)
|
||||||
|
try:
|
||||||
|
dumper.open()
|
||||||
|
for data in documents:
|
||||||
|
dumper.represent(data)
|
||||||
|
dumper.close()
|
||||||
|
finally:
|
||||||
|
dumper.dispose()
|
||||||
|
if getvalue:
|
||||||
|
return getvalue()
|
||||||
|
|
||||||
|
def dump(data, stream=None, Dumper=Dumper, **kwds):
|
||||||
|
"""
|
||||||
|
Serialize a Python object into a YAML stream.
|
||||||
|
If stream is None, return the produced string instead.
|
||||||
|
"""
|
||||||
|
return dump_all([data], stream, Dumper=Dumper, **kwds)
|
||||||
|
|
||||||
|
def safe_dump_all(documents, stream=None, **kwds):
|
||||||
|
"""
|
||||||
|
Serialize a sequence of Python objects into a YAML stream.
|
||||||
|
Produce only basic YAML tags.
|
||||||
|
If stream is None, return the produced string instead.
|
||||||
|
"""
|
||||||
|
return dump_all(documents, stream, Dumper=SafeDumper, **kwds)
|
||||||
|
|
||||||
|
def safe_dump(data, stream=None, **kwds):
|
||||||
|
"""
|
||||||
|
Serialize a Python object into a YAML stream.
|
||||||
|
Produce only basic YAML tags.
|
||||||
|
If stream is None, return the produced string instead.
|
||||||
|
"""
|
||||||
|
return dump_all([data], stream, Dumper=SafeDumper, **kwds)
|
||||||
|
|
||||||
|
def add_implicit_resolver(tag, regexp, first=None,
|
||||||
|
Loader=None, Dumper=Dumper):
|
||||||
|
"""
|
||||||
|
Add an implicit scalar detector.
|
||||||
|
If an implicit scalar value matches the given regexp,
|
||||||
|
the corresponding tag is assigned to the scalar.
|
||||||
|
first is a sequence of possible initial characters or None.
|
||||||
|
"""
|
||||||
|
if Loader is None:
|
||||||
|
loader.Loader.add_implicit_resolver(tag, regexp, first)
|
||||||
|
loader.FullLoader.add_implicit_resolver(tag, regexp, first)
|
||||||
|
loader.UnsafeLoader.add_implicit_resolver(tag, regexp, first)
|
||||||
|
else:
|
||||||
|
Loader.add_implicit_resolver(tag, regexp, first)
|
||||||
|
Dumper.add_implicit_resolver(tag, regexp, first)
|
||||||
|
|
||||||
|
def add_path_resolver(tag, path, kind=None, Loader=None, Dumper=Dumper):
|
||||||
|
"""
|
||||||
|
Add a path based resolver for the given tag.
|
||||||
|
A path is a list of keys that forms a path
|
||||||
|
to a node in the representation tree.
|
||||||
|
Keys can be string values, integers, or None.
|
||||||
|
"""
|
||||||
|
if Loader is None:
|
||||||
|
loader.Loader.add_path_resolver(tag, path, kind)
|
||||||
|
loader.FullLoader.add_path_resolver(tag, path, kind)
|
||||||
|
loader.UnsafeLoader.add_path_resolver(tag, path, kind)
|
||||||
|
else:
|
||||||
|
Loader.add_path_resolver(tag, path, kind)
|
||||||
|
Dumper.add_path_resolver(tag, path, kind)
|
||||||
|
|
||||||
|
def add_constructor(tag, constructor, Loader=None):
|
||||||
|
"""
|
||||||
|
Add a constructor for the given tag.
|
||||||
|
Constructor is a function that accepts a Loader instance
|
||||||
|
and a node object and produces the corresponding Python object.
|
||||||
|
"""
|
||||||
|
if Loader is None:
|
||||||
|
loader.Loader.add_constructor(tag, constructor)
|
||||||
|
loader.FullLoader.add_constructor(tag, constructor)
|
||||||
|
loader.UnsafeLoader.add_constructor(tag, constructor)
|
||||||
|
else:
|
||||||
|
Loader.add_constructor(tag, constructor)
|
||||||
|
|
||||||
|
def add_multi_constructor(tag_prefix, multi_constructor, Loader=None):
|
||||||
|
"""
|
||||||
|
Add a multi-constructor for the given tag prefix.
|
||||||
|
Multi-constructor is called for a node if its tag starts with tag_prefix.
|
||||||
|
Multi-constructor accepts a Loader instance, a tag suffix,
|
||||||
|
and a node object and produces the corresponding Python object.
|
||||||
|
"""
|
||||||
|
if Loader is None:
|
||||||
|
loader.Loader.add_multi_constructor(tag_prefix, multi_constructor)
|
||||||
|
loader.FullLoader.add_multi_constructor(tag_prefix, multi_constructor)
|
||||||
|
loader.UnsafeLoader.add_multi_constructor(tag_prefix, multi_constructor)
|
||||||
|
else:
|
||||||
|
Loader.add_multi_constructor(tag_prefix, multi_constructor)
|
||||||
|
|
||||||
|
def add_representer(data_type, representer, Dumper=Dumper):
|
||||||
|
"""
|
||||||
|
Add a representer for the given type.
|
||||||
|
Representer is a function accepting a Dumper instance
|
||||||
|
and an instance of the given data type
|
||||||
|
and producing the corresponding representation node.
|
||||||
|
"""
|
||||||
|
Dumper.add_representer(data_type, representer)
|
||||||
|
|
||||||
|
def add_multi_representer(data_type, multi_representer, Dumper=Dumper):
|
||||||
|
"""
|
||||||
|
Add a representer for the given type.
|
||||||
|
Multi-representer is a function accepting a Dumper instance
|
||||||
|
and an instance of the given data type or subtype
|
||||||
|
and producing the corresponding representation node.
|
||||||
|
"""
|
||||||
|
Dumper.add_multi_representer(data_type, multi_representer)
|
||||||
|
|
||||||
|
class YAMLObjectMetaclass(type):
|
||||||
|
"""
|
||||||
|
The metaclass for YAMLObject.
|
||||||
|
"""
|
||||||
|
def __init__(cls, name, bases, kwds):
|
||||||
|
super(YAMLObjectMetaclass, cls).__init__(name, bases, kwds)
|
||||||
|
if 'yaml_tag' in kwds and kwds['yaml_tag'] is not None:
|
||||||
|
if isinstance(cls.yaml_loader, list):
|
||||||
|
for loader in cls.yaml_loader:
|
||||||
|
loader.add_constructor(cls.yaml_tag, cls.from_yaml)
|
||||||
|
else:
|
||||||
|
cls.yaml_loader.add_constructor(cls.yaml_tag, cls.from_yaml)
|
||||||
|
|
||||||
|
cls.yaml_dumper.add_representer(cls, cls.to_yaml)
|
||||||
|
|
||||||
|
class YAMLObject(metaclass=YAMLObjectMetaclass):
|
||||||
|
"""
|
||||||
|
An object that can dump itself to a YAML stream
|
||||||
|
and load itself from a YAML stream.
|
||||||
|
"""
|
||||||
|
|
||||||
|
__slots__ = () # no direct instantiation, so allow immutable subclasses
|
||||||
|
|
||||||
|
yaml_loader = [Loader, FullLoader, UnsafeLoader]
|
||||||
|
yaml_dumper = Dumper
|
||||||
|
|
||||||
|
yaml_tag = None
|
||||||
|
yaml_flow_style = None
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_yaml(cls, loader, node):
|
||||||
|
"""
|
||||||
|
Convert a representation node to a Python object.
|
||||||
|
"""
|
||||||
|
return loader.construct_yaml_object(node, cls)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def to_yaml(cls, dumper, data):
|
||||||
|
"""
|
||||||
|
Convert a Python object to a representation node.
|
||||||
|
"""
|
||||||
|
return dumper.represent_yaml_object(cls.yaml_tag, data, cls,
|
||||||
|
flow_style=cls.yaml_flow_style)
|
||||||
|
|
||||||
139
lib/sunhpc/yaml/composer.py
Normal file
139
lib/sunhpc/yaml/composer.py
Normal file
@@ -0,0 +1,139 @@
|
|||||||
|
|
||||||
|
__all__ = ['Composer', 'ComposerError']
|
||||||
|
|
||||||
|
from .error import MarkedYAMLError
|
||||||
|
from .events import *
|
||||||
|
from .nodes import *
|
||||||
|
|
||||||
|
class ComposerError(MarkedYAMLError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class Composer:
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.anchors = {}
|
||||||
|
|
||||||
|
def check_node(self):
|
||||||
|
# Drop the STREAM-START event.
|
||||||
|
if self.check_event(StreamStartEvent):
|
||||||
|
self.get_event()
|
||||||
|
|
||||||
|
# If there are more documents available?
|
||||||
|
return not self.check_event(StreamEndEvent)
|
||||||
|
|
||||||
|
def get_node(self):
|
||||||
|
# Get the root node of the next document.
|
||||||
|
if not self.check_event(StreamEndEvent):
|
||||||
|
return self.compose_document()
|
||||||
|
|
||||||
|
def get_single_node(self):
|
||||||
|
# Drop the STREAM-START event.
|
||||||
|
self.get_event()
|
||||||
|
|
||||||
|
# Compose a document if the stream is not empty.
|
||||||
|
document = None
|
||||||
|
if not self.check_event(StreamEndEvent):
|
||||||
|
document = self.compose_document()
|
||||||
|
|
||||||
|
# Ensure that the stream contains no more documents.
|
||||||
|
if not self.check_event(StreamEndEvent):
|
||||||
|
event = self.get_event()
|
||||||
|
raise ComposerError("expected a single document in the stream",
|
||||||
|
document.start_mark, "but found another document",
|
||||||
|
event.start_mark)
|
||||||
|
|
||||||
|
# Drop the STREAM-END event.
|
||||||
|
self.get_event()
|
||||||
|
|
||||||
|
return document
|
||||||
|
|
||||||
|
def compose_document(self):
|
||||||
|
# Drop the DOCUMENT-START event.
|
||||||
|
self.get_event()
|
||||||
|
|
||||||
|
# Compose the root node.
|
||||||
|
node = self.compose_node(None, None)
|
||||||
|
|
||||||
|
# Drop the DOCUMENT-END event.
|
||||||
|
self.get_event()
|
||||||
|
|
||||||
|
self.anchors = {}
|
||||||
|
return node
|
||||||
|
|
||||||
|
def compose_node(self, parent, index):
|
||||||
|
if self.check_event(AliasEvent):
|
||||||
|
event = self.get_event()
|
||||||
|
anchor = event.anchor
|
||||||
|
if anchor not in self.anchors:
|
||||||
|
raise ComposerError(None, None, "found undefined alias %r"
|
||||||
|
% anchor, event.start_mark)
|
||||||
|
return self.anchors[anchor]
|
||||||
|
event = self.peek_event()
|
||||||
|
anchor = event.anchor
|
||||||
|
if anchor is not None:
|
||||||
|
if anchor in self.anchors:
|
||||||
|
raise ComposerError("found duplicate anchor %r; first occurrence"
|
||||||
|
% anchor, self.anchors[anchor].start_mark,
|
||||||
|
"second occurrence", event.start_mark)
|
||||||
|
self.descend_resolver(parent, index)
|
||||||
|
if self.check_event(ScalarEvent):
|
||||||
|
node = self.compose_scalar_node(anchor)
|
||||||
|
elif self.check_event(SequenceStartEvent):
|
||||||
|
node = self.compose_sequence_node(anchor)
|
||||||
|
elif self.check_event(MappingStartEvent):
|
||||||
|
node = self.compose_mapping_node(anchor)
|
||||||
|
self.ascend_resolver()
|
||||||
|
return node
|
||||||
|
|
||||||
|
def compose_scalar_node(self, anchor):
|
||||||
|
event = self.get_event()
|
||||||
|
tag = event.tag
|
||||||
|
if tag is None or tag == '!':
|
||||||
|
tag = self.resolve(ScalarNode, event.value, event.implicit)
|
||||||
|
node = ScalarNode(tag, event.value,
|
||||||
|
event.start_mark, event.end_mark, style=event.style)
|
||||||
|
if anchor is not None:
|
||||||
|
self.anchors[anchor] = node
|
||||||
|
return node
|
||||||
|
|
||||||
|
def compose_sequence_node(self, anchor):
|
||||||
|
start_event = self.get_event()
|
||||||
|
tag = start_event.tag
|
||||||
|
if tag is None or tag == '!':
|
||||||
|
tag = self.resolve(SequenceNode, None, start_event.implicit)
|
||||||
|
node = SequenceNode(tag, [],
|
||||||
|
start_event.start_mark, None,
|
||||||
|
flow_style=start_event.flow_style)
|
||||||
|
if anchor is not None:
|
||||||
|
self.anchors[anchor] = node
|
||||||
|
index = 0
|
||||||
|
while not self.check_event(SequenceEndEvent):
|
||||||
|
node.value.append(self.compose_node(node, index))
|
||||||
|
index += 1
|
||||||
|
end_event = self.get_event()
|
||||||
|
node.end_mark = end_event.end_mark
|
||||||
|
return node
|
||||||
|
|
||||||
|
def compose_mapping_node(self, anchor):
|
||||||
|
start_event = self.get_event()
|
||||||
|
tag = start_event.tag
|
||||||
|
if tag is None or tag == '!':
|
||||||
|
tag = self.resolve(MappingNode, None, start_event.implicit)
|
||||||
|
node = MappingNode(tag, [],
|
||||||
|
start_event.start_mark, None,
|
||||||
|
flow_style=start_event.flow_style)
|
||||||
|
if anchor is not None:
|
||||||
|
self.anchors[anchor] = node
|
||||||
|
while not self.check_event(MappingEndEvent):
|
||||||
|
#key_event = self.peek_event()
|
||||||
|
item_key = self.compose_node(node, None)
|
||||||
|
#if item_key in node.value:
|
||||||
|
# raise ComposerError("while composing a mapping", start_event.start_mark,
|
||||||
|
# "found duplicate key", key_event.start_mark)
|
||||||
|
item_value = self.compose_node(node, item_key)
|
||||||
|
#node.value[item_key] = item_value
|
||||||
|
node.value.append((item_key, item_value))
|
||||||
|
end_event = self.get_event()
|
||||||
|
node.end_mark = end_event.end_mark
|
||||||
|
return node
|
||||||
|
|
||||||
748
lib/sunhpc/yaml/constructor.py
Normal file
748
lib/sunhpc/yaml/constructor.py
Normal file
@@ -0,0 +1,748 @@
|
|||||||
|
|
||||||
|
__all__ = [
|
||||||
|
'BaseConstructor',
|
||||||
|
'SafeConstructor',
|
||||||
|
'FullConstructor',
|
||||||
|
'UnsafeConstructor',
|
||||||
|
'Constructor',
|
||||||
|
'ConstructorError'
|
||||||
|
]
|
||||||
|
|
||||||
|
from .error import *
|
||||||
|
from .nodes import *
|
||||||
|
|
||||||
|
import collections.abc, datetime, base64, binascii, re, sys, types
|
||||||
|
|
||||||
|
class ConstructorError(MarkedYAMLError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class BaseConstructor:
|
||||||
|
|
||||||
|
yaml_constructors = {}
|
||||||
|
yaml_multi_constructors = {}
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.constructed_objects = {}
|
||||||
|
self.recursive_objects = {}
|
||||||
|
self.state_generators = []
|
||||||
|
self.deep_construct = False
|
||||||
|
|
||||||
|
def check_data(self):
|
||||||
|
# If there are more documents available?
|
||||||
|
return self.check_node()
|
||||||
|
|
||||||
|
def check_state_key(self, key):
|
||||||
|
"""Block special attributes/methods from being set in a newly created
|
||||||
|
object, to prevent user-controlled methods from being called during
|
||||||
|
deserialization"""
|
||||||
|
if self.get_state_keys_blacklist_regexp().match(key):
|
||||||
|
raise ConstructorError(None, None,
|
||||||
|
"blacklisted key '%s' in instance state found" % (key,), None)
|
||||||
|
|
||||||
|
def get_data(self):
|
||||||
|
# Construct and return the next document.
|
||||||
|
if self.check_node():
|
||||||
|
return self.construct_document(self.get_node())
|
||||||
|
|
||||||
|
def get_single_data(self):
|
||||||
|
# Ensure that the stream contains a single document and construct it.
|
||||||
|
node = self.get_single_node()
|
||||||
|
if node is not None:
|
||||||
|
return self.construct_document(node)
|
||||||
|
return None
|
||||||
|
|
||||||
|
def construct_document(self, node):
|
||||||
|
data = self.construct_object(node)
|
||||||
|
while self.state_generators:
|
||||||
|
state_generators = self.state_generators
|
||||||
|
self.state_generators = []
|
||||||
|
for generator in state_generators:
|
||||||
|
for dummy in generator:
|
||||||
|
pass
|
||||||
|
self.constructed_objects = {}
|
||||||
|
self.recursive_objects = {}
|
||||||
|
self.deep_construct = False
|
||||||
|
return data
|
||||||
|
|
||||||
|
def construct_object(self, node, deep=False):
|
||||||
|
if node in self.constructed_objects:
|
||||||
|
return self.constructed_objects[node]
|
||||||
|
if deep:
|
||||||
|
old_deep = self.deep_construct
|
||||||
|
self.deep_construct = True
|
||||||
|
if node in self.recursive_objects:
|
||||||
|
raise ConstructorError(None, None,
|
||||||
|
"found unconstructable recursive node", node.start_mark)
|
||||||
|
self.recursive_objects[node] = None
|
||||||
|
constructor = None
|
||||||
|
tag_suffix = None
|
||||||
|
if node.tag in self.yaml_constructors:
|
||||||
|
constructor = self.yaml_constructors[node.tag]
|
||||||
|
else:
|
||||||
|
for tag_prefix in self.yaml_multi_constructors:
|
||||||
|
if tag_prefix is not None and node.tag.startswith(tag_prefix):
|
||||||
|
tag_suffix = node.tag[len(tag_prefix):]
|
||||||
|
constructor = self.yaml_multi_constructors[tag_prefix]
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
if None in self.yaml_multi_constructors:
|
||||||
|
tag_suffix = node.tag
|
||||||
|
constructor = self.yaml_multi_constructors[None]
|
||||||
|
elif None in self.yaml_constructors:
|
||||||
|
constructor = self.yaml_constructors[None]
|
||||||
|
elif isinstance(node, ScalarNode):
|
||||||
|
constructor = self.__class__.construct_scalar
|
||||||
|
elif isinstance(node, SequenceNode):
|
||||||
|
constructor = self.__class__.construct_sequence
|
||||||
|
elif isinstance(node, MappingNode):
|
||||||
|
constructor = self.__class__.construct_mapping
|
||||||
|
if tag_suffix is None:
|
||||||
|
data = constructor(self, node)
|
||||||
|
else:
|
||||||
|
data = constructor(self, tag_suffix, node)
|
||||||
|
if isinstance(data, types.GeneratorType):
|
||||||
|
generator = data
|
||||||
|
data = next(generator)
|
||||||
|
if self.deep_construct:
|
||||||
|
for dummy in generator:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
self.state_generators.append(generator)
|
||||||
|
self.constructed_objects[node] = data
|
||||||
|
del self.recursive_objects[node]
|
||||||
|
if deep:
|
||||||
|
self.deep_construct = old_deep
|
||||||
|
return data
|
||||||
|
|
||||||
|
def construct_scalar(self, node):
|
||||||
|
if not isinstance(node, ScalarNode):
|
||||||
|
raise ConstructorError(None, None,
|
||||||
|
"expected a scalar node, but found %s" % node.id,
|
||||||
|
node.start_mark)
|
||||||
|
return node.value
|
||||||
|
|
||||||
|
def construct_sequence(self, node, deep=False):
|
||||||
|
if not isinstance(node, SequenceNode):
|
||||||
|
raise ConstructorError(None, None,
|
||||||
|
"expected a sequence node, but found %s" % node.id,
|
||||||
|
node.start_mark)
|
||||||
|
return [self.construct_object(child, deep=deep)
|
||||||
|
for child in node.value]
|
||||||
|
|
||||||
|
def construct_mapping(self, node, deep=False):
|
||||||
|
if not isinstance(node, MappingNode):
|
||||||
|
raise ConstructorError(None, None,
|
||||||
|
"expected a mapping node, but found %s" % node.id,
|
||||||
|
node.start_mark)
|
||||||
|
mapping = {}
|
||||||
|
for key_node, value_node in node.value:
|
||||||
|
key = self.construct_object(key_node, deep=deep)
|
||||||
|
if not isinstance(key, collections.abc.Hashable):
|
||||||
|
raise ConstructorError("while constructing a mapping", node.start_mark,
|
||||||
|
"found unhashable key", key_node.start_mark)
|
||||||
|
value = self.construct_object(value_node, deep=deep)
|
||||||
|
mapping[key] = value
|
||||||
|
return mapping
|
||||||
|
|
||||||
|
def construct_pairs(self, node, deep=False):
|
||||||
|
if not isinstance(node, MappingNode):
|
||||||
|
raise ConstructorError(None, None,
|
||||||
|
"expected a mapping node, but found %s" % node.id,
|
||||||
|
node.start_mark)
|
||||||
|
pairs = []
|
||||||
|
for key_node, value_node in node.value:
|
||||||
|
key = self.construct_object(key_node, deep=deep)
|
||||||
|
value = self.construct_object(value_node, deep=deep)
|
||||||
|
pairs.append((key, value))
|
||||||
|
return pairs
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def add_constructor(cls, tag, constructor):
|
||||||
|
if not 'yaml_constructors' in cls.__dict__:
|
||||||
|
cls.yaml_constructors = cls.yaml_constructors.copy()
|
||||||
|
cls.yaml_constructors[tag] = constructor
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def add_multi_constructor(cls, tag_prefix, multi_constructor):
|
||||||
|
if not 'yaml_multi_constructors' in cls.__dict__:
|
||||||
|
cls.yaml_multi_constructors = cls.yaml_multi_constructors.copy()
|
||||||
|
cls.yaml_multi_constructors[tag_prefix] = multi_constructor
|
||||||
|
|
||||||
|
class SafeConstructor(BaseConstructor):
|
||||||
|
|
||||||
|
def construct_scalar(self, node):
|
||||||
|
if isinstance(node, MappingNode):
|
||||||
|
for key_node, value_node in node.value:
|
||||||
|
if key_node.tag == 'tag:yaml.org,2002:value':
|
||||||
|
return self.construct_scalar(value_node)
|
||||||
|
return super().construct_scalar(node)
|
||||||
|
|
||||||
|
def flatten_mapping(self, node):
|
||||||
|
merge = []
|
||||||
|
index = 0
|
||||||
|
while index < len(node.value):
|
||||||
|
key_node, value_node = node.value[index]
|
||||||
|
if key_node.tag == 'tag:yaml.org,2002:merge':
|
||||||
|
del node.value[index]
|
||||||
|
if isinstance(value_node, MappingNode):
|
||||||
|
self.flatten_mapping(value_node)
|
||||||
|
merge.extend(value_node.value)
|
||||||
|
elif isinstance(value_node, SequenceNode):
|
||||||
|
submerge = []
|
||||||
|
for subnode in value_node.value:
|
||||||
|
if not isinstance(subnode, MappingNode):
|
||||||
|
raise ConstructorError("while constructing a mapping",
|
||||||
|
node.start_mark,
|
||||||
|
"expected a mapping for merging, but found %s"
|
||||||
|
% subnode.id, subnode.start_mark)
|
||||||
|
self.flatten_mapping(subnode)
|
||||||
|
submerge.append(subnode.value)
|
||||||
|
submerge.reverse()
|
||||||
|
for value in submerge:
|
||||||
|
merge.extend(value)
|
||||||
|
else:
|
||||||
|
raise ConstructorError("while constructing a mapping", node.start_mark,
|
||||||
|
"expected a mapping or list of mappings for merging, but found %s"
|
||||||
|
% value_node.id, value_node.start_mark)
|
||||||
|
elif key_node.tag == 'tag:yaml.org,2002:value':
|
||||||
|
key_node.tag = 'tag:yaml.org,2002:str'
|
||||||
|
index += 1
|
||||||
|
else:
|
||||||
|
index += 1
|
||||||
|
if merge:
|
||||||
|
node.value = merge + node.value
|
||||||
|
|
||||||
|
def construct_mapping(self, node, deep=False):
|
||||||
|
if isinstance(node, MappingNode):
|
||||||
|
self.flatten_mapping(node)
|
||||||
|
return super().construct_mapping(node, deep=deep)
|
||||||
|
|
||||||
|
def construct_yaml_null(self, node):
|
||||||
|
self.construct_scalar(node)
|
||||||
|
return None
|
||||||
|
|
||||||
|
bool_values = {
|
||||||
|
'yes': True,
|
||||||
|
'no': False,
|
||||||
|
'true': True,
|
||||||
|
'false': False,
|
||||||
|
'on': True,
|
||||||
|
'off': False,
|
||||||
|
}
|
||||||
|
|
||||||
|
def construct_yaml_bool(self, node):
|
||||||
|
value = self.construct_scalar(node)
|
||||||
|
return self.bool_values[value.lower()]
|
||||||
|
|
||||||
|
def construct_yaml_int(self, node):
|
||||||
|
value = self.construct_scalar(node)
|
||||||
|
value = value.replace('_', '')
|
||||||
|
sign = +1
|
||||||
|
if value[0] == '-':
|
||||||
|
sign = -1
|
||||||
|
if value[0] in '+-':
|
||||||
|
value = value[1:]
|
||||||
|
if value == '0':
|
||||||
|
return 0
|
||||||
|
elif value.startswith('0b'):
|
||||||
|
return sign*int(value[2:], 2)
|
||||||
|
elif value.startswith('0x'):
|
||||||
|
return sign*int(value[2:], 16)
|
||||||
|
elif value[0] == '0':
|
||||||
|
return sign*int(value, 8)
|
||||||
|
elif ':' in value:
|
||||||
|
digits = [int(part) for part in value.split(':')]
|
||||||
|
digits.reverse()
|
||||||
|
base = 1
|
||||||
|
value = 0
|
||||||
|
for digit in digits:
|
||||||
|
value += digit*base
|
||||||
|
base *= 60
|
||||||
|
return sign*value
|
||||||
|
else:
|
||||||
|
return sign*int(value)
|
||||||
|
|
||||||
|
inf_value = 1e300
|
||||||
|
while inf_value != inf_value*inf_value:
|
||||||
|
inf_value *= inf_value
|
||||||
|
nan_value = -inf_value/inf_value # Trying to make a quiet NaN (like C99).
|
||||||
|
|
||||||
|
def construct_yaml_float(self, node):
|
||||||
|
value = self.construct_scalar(node)
|
||||||
|
value = value.replace('_', '').lower()
|
||||||
|
sign = +1
|
||||||
|
if value[0] == '-':
|
||||||
|
sign = -1
|
||||||
|
if value[0] in '+-':
|
||||||
|
value = value[1:]
|
||||||
|
if value == '.inf':
|
||||||
|
return sign*self.inf_value
|
||||||
|
elif value == '.nan':
|
||||||
|
return self.nan_value
|
||||||
|
elif ':' in value:
|
||||||
|
digits = [float(part) for part in value.split(':')]
|
||||||
|
digits.reverse()
|
||||||
|
base = 1
|
||||||
|
value = 0.0
|
||||||
|
for digit in digits:
|
||||||
|
value += digit*base
|
||||||
|
base *= 60
|
||||||
|
return sign*value
|
||||||
|
else:
|
||||||
|
return sign*float(value)
|
||||||
|
|
||||||
|
def construct_yaml_binary(self, node):
|
||||||
|
try:
|
||||||
|
value = self.construct_scalar(node).encode('ascii')
|
||||||
|
except UnicodeEncodeError as exc:
|
||||||
|
raise ConstructorError(None, None,
|
||||||
|
"failed to convert base64 data into ascii: %s" % exc,
|
||||||
|
node.start_mark)
|
||||||
|
try:
|
||||||
|
if hasattr(base64, 'decodebytes'):
|
||||||
|
return base64.decodebytes(value)
|
||||||
|
else:
|
||||||
|
return base64.decodestring(value)
|
||||||
|
except binascii.Error as exc:
|
||||||
|
raise ConstructorError(None, None,
|
||||||
|
"failed to decode base64 data: %s" % exc, node.start_mark)
|
||||||
|
|
||||||
|
timestamp_regexp = re.compile(
|
||||||
|
r'''^(?P<year>[0-9][0-9][0-9][0-9])
|
||||||
|
-(?P<month>[0-9][0-9]?)
|
||||||
|
-(?P<day>[0-9][0-9]?)
|
||||||
|
(?:(?:[Tt]|[ \t]+)
|
||||||
|
(?P<hour>[0-9][0-9]?)
|
||||||
|
:(?P<minute>[0-9][0-9])
|
||||||
|
:(?P<second>[0-9][0-9])
|
||||||
|
(?:\.(?P<fraction>[0-9]*))?
|
||||||
|
(?:[ \t]*(?P<tz>Z|(?P<tz_sign>[-+])(?P<tz_hour>[0-9][0-9]?)
|
||||||
|
(?::(?P<tz_minute>[0-9][0-9]))?))?)?$''', re.X)
|
||||||
|
|
||||||
|
def construct_yaml_timestamp(self, node):
|
||||||
|
value = self.construct_scalar(node)
|
||||||
|
match = self.timestamp_regexp.match(node.value)
|
||||||
|
values = match.groupdict()
|
||||||
|
year = int(values['year'])
|
||||||
|
month = int(values['month'])
|
||||||
|
day = int(values['day'])
|
||||||
|
if not values['hour']:
|
||||||
|
return datetime.date(year, month, day)
|
||||||
|
hour = int(values['hour'])
|
||||||
|
minute = int(values['minute'])
|
||||||
|
second = int(values['second'])
|
||||||
|
fraction = 0
|
||||||
|
tzinfo = None
|
||||||
|
if values['fraction']:
|
||||||
|
fraction = values['fraction'][:6]
|
||||||
|
while len(fraction) < 6:
|
||||||
|
fraction += '0'
|
||||||
|
fraction = int(fraction)
|
||||||
|
if values['tz_sign']:
|
||||||
|
tz_hour = int(values['tz_hour'])
|
||||||
|
tz_minute = int(values['tz_minute'] or 0)
|
||||||
|
delta = datetime.timedelta(hours=tz_hour, minutes=tz_minute)
|
||||||
|
if values['tz_sign'] == '-':
|
||||||
|
delta = -delta
|
||||||
|
tzinfo = datetime.timezone(delta)
|
||||||
|
elif values['tz']:
|
||||||
|
tzinfo = datetime.timezone.utc
|
||||||
|
return datetime.datetime(year, month, day, hour, minute, second, fraction,
|
||||||
|
tzinfo=tzinfo)
|
||||||
|
|
||||||
|
def construct_yaml_omap(self, node):
|
||||||
|
# Note: we do not check for duplicate keys, because it's too
|
||||||
|
# CPU-expensive.
|
||||||
|
omap = []
|
||||||
|
yield omap
|
||||||
|
if not isinstance(node, SequenceNode):
|
||||||
|
raise ConstructorError("while constructing an ordered map", node.start_mark,
|
||||||
|
"expected a sequence, but found %s" % node.id, node.start_mark)
|
||||||
|
for subnode in node.value:
|
||||||
|
if not isinstance(subnode, MappingNode):
|
||||||
|
raise ConstructorError("while constructing an ordered map", node.start_mark,
|
||||||
|
"expected a mapping of length 1, but found %s" % subnode.id,
|
||||||
|
subnode.start_mark)
|
||||||
|
if len(subnode.value) != 1:
|
||||||
|
raise ConstructorError("while constructing an ordered map", node.start_mark,
|
||||||
|
"expected a single mapping item, but found %d items" % len(subnode.value),
|
||||||
|
subnode.start_mark)
|
||||||
|
key_node, value_node = subnode.value[0]
|
||||||
|
key = self.construct_object(key_node)
|
||||||
|
value = self.construct_object(value_node)
|
||||||
|
omap.append((key, value))
|
||||||
|
|
||||||
|
def construct_yaml_pairs(self, node):
|
||||||
|
# Note: the same code as `construct_yaml_omap`.
|
||||||
|
pairs = []
|
||||||
|
yield pairs
|
||||||
|
if not isinstance(node, SequenceNode):
|
||||||
|
raise ConstructorError("while constructing pairs", node.start_mark,
|
||||||
|
"expected a sequence, but found %s" % node.id, node.start_mark)
|
||||||
|
for subnode in node.value:
|
||||||
|
if not isinstance(subnode, MappingNode):
|
||||||
|
raise ConstructorError("while constructing pairs", node.start_mark,
|
||||||
|
"expected a mapping of length 1, but found %s" % subnode.id,
|
||||||
|
subnode.start_mark)
|
||||||
|
if len(subnode.value) != 1:
|
||||||
|
raise ConstructorError("while constructing pairs", node.start_mark,
|
||||||
|
"expected a single mapping item, but found %d items" % len(subnode.value),
|
||||||
|
subnode.start_mark)
|
||||||
|
key_node, value_node = subnode.value[0]
|
||||||
|
key = self.construct_object(key_node)
|
||||||
|
value = self.construct_object(value_node)
|
||||||
|
pairs.append((key, value))
|
||||||
|
|
||||||
|
def construct_yaml_set(self, node):
|
||||||
|
data = set()
|
||||||
|
yield data
|
||||||
|
value = self.construct_mapping(node)
|
||||||
|
data.update(value)
|
||||||
|
|
||||||
|
def construct_yaml_str(self, node):
|
||||||
|
return self.construct_scalar(node)
|
||||||
|
|
||||||
|
def construct_yaml_seq(self, node):
|
||||||
|
data = []
|
||||||
|
yield data
|
||||||
|
data.extend(self.construct_sequence(node))
|
||||||
|
|
||||||
|
def construct_yaml_map(self, node):
|
||||||
|
data = {}
|
||||||
|
yield data
|
||||||
|
value = self.construct_mapping(node)
|
||||||
|
data.update(value)
|
||||||
|
|
||||||
|
def construct_yaml_object(self, node, cls):
|
||||||
|
data = cls.__new__(cls)
|
||||||
|
yield data
|
||||||
|
if hasattr(data, '__setstate__'):
|
||||||
|
state = self.construct_mapping(node, deep=True)
|
||||||
|
data.__setstate__(state)
|
||||||
|
else:
|
||||||
|
state = self.construct_mapping(node)
|
||||||
|
data.__dict__.update(state)
|
||||||
|
|
||||||
|
def construct_undefined(self, node):
|
||||||
|
raise ConstructorError(None, None,
|
||||||
|
"could not determine a constructor for the tag %r" % node.tag,
|
||||||
|
node.start_mark)
|
||||||
|
|
||||||
|
SafeConstructor.add_constructor(
|
||||||
|
'tag:yaml.org,2002:null',
|
||||||
|
SafeConstructor.construct_yaml_null)
|
||||||
|
|
||||||
|
SafeConstructor.add_constructor(
|
||||||
|
'tag:yaml.org,2002:bool',
|
||||||
|
SafeConstructor.construct_yaml_bool)
|
||||||
|
|
||||||
|
SafeConstructor.add_constructor(
|
||||||
|
'tag:yaml.org,2002:int',
|
||||||
|
SafeConstructor.construct_yaml_int)
|
||||||
|
|
||||||
|
SafeConstructor.add_constructor(
|
||||||
|
'tag:yaml.org,2002:float',
|
||||||
|
SafeConstructor.construct_yaml_float)
|
||||||
|
|
||||||
|
SafeConstructor.add_constructor(
|
||||||
|
'tag:yaml.org,2002:binary',
|
||||||
|
SafeConstructor.construct_yaml_binary)
|
||||||
|
|
||||||
|
SafeConstructor.add_constructor(
|
||||||
|
'tag:yaml.org,2002:timestamp',
|
||||||
|
SafeConstructor.construct_yaml_timestamp)
|
||||||
|
|
||||||
|
SafeConstructor.add_constructor(
|
||||||
|
'tag:yaml.org,2002:omap',
|
||||||
|
SafeConstructor.construct_yaml_omap)
|
||||||
|
|
||||||
|
SafeConstructor.add_constructor(
|
||||||
|
'tag:yaml.org,2002:pairs',
|
||||||
|
SafeConstructor.construct_yaml_pairs)
|
||||||
|
|
||||||
|
SafeConstructor.add_constructor(
|
||||||
|
'tag:yaml.org,2002:set',
|
||||||
|
SafeConstructor.construct_yaml_set)
|
||||||
|
|
||||||
|
SafeConstructor.add_constructor(
|
||||||
|
'tag:yaml.org,2002:str',
|
||||||
|
SafeConstructor.construct_yaml_str)
|
||||||
|
|
||||||
|
SafeConstructor.add_constructor(
|
||||||
|
'tag:yaml.org,2002:seq',
|
||||||
|
SafeConstructor.construct_yaml_seq)
|
||||||
|
|
||||||
|
SafeConstructor.add_constructor(
|
||||||
|
'tag:yaml.org,2002:map',
|
||||||
|
SafeConstructor.construct_yaml_map)
|
||||||
|
|
||||||
|
SafeConstructor.add_constructor(None,
|
||||||
|
SafeConstructor.construct_undefined)
|
||||||
|
|
||||||
|
class FullConstructor(SafeConstructor):
|
||||||
|
# 'extend' is blacklisted because it is used by
|
||||||
|
# construct_python_object_apply to add `listitems` to a newly generate
|
||||||
|
# python instance
|
||||||
|
def get_state_keys_blacklist(self):
|
||||||
|
return ['^extend$', '^__.*__$']
|
||||||
|
|
||||||
|
def get_state_keys_blacklist_regexp(self):
|
||||||
|
if not hasattr(self, 'state_keys_blacklist_regexp'):
|
||||||
|
self.state_keys_blacklist_regexp = re.compile('(' + '|'.join(self.get_state_keys_blacklist()) + ')')
|
||||||
|
return self.state_keys_blacklist_regexp
|
||||||
|
|
||||||
|
def construct_python_str(self, node):
|
||||||
|
return self.construct_scalar(node)
|
||||||
|
|
||||||
|
def construct_python_unicode(self, node):
|
||||||
|
return self.construct_scalar(node)
|
||||||
|
|
||||||
|
def construct_python_bytes(self, node):
|
||||||
|
try:
|
||||||
|
value = self.construct_scalar(node).encode('ascii')
|
||||||
|
except UnicodeEncodeError as exc:
|
||||||
|
raise ConstructorError(None, None,
|
||||||
|
"failed to convert base64 data into ascii: %s" % exc,
|
||||||
|
node.start_mark)
|
||||||
|
try:
|
||||||
|
if hasattr(base64, 'decodebytes'):
|
||||||
|
return base64.decodebytes(value)
|
||||||
|
else:
|
||||||
|
return base64.decodestring(value)
|
||||||
|
except binascii.Error as exc:
|
||||||
|
raise ConstructorError(None, None,
|
||||||
|
"failed to decode base64 data: %s" % exc, node.start_mark)
|
||||||
|
|
||||||
|
def construct_python_long(self, node):
|
||||||
|
return self.construct_yaml_int(node)
|
||||||
|
|
||||||
|
def construct_python_complex(self, node):
|
||||||
|
return complex(self.construct_scalar(node))
|
||||||
|
|
||||||
|
def construct_python_tuple(self, node):
|
||||||
|
return tuple(self.construct_sequence(node))
|
||||||
|
|
||||||
|
def find_python_module(self, name, mark, unsafe=False):
|
||||||
|
if not name:
|
||||||
|
raise ConstructorError("while constructing a Python module", mark,
|
||||||
|
"expected non-empty name appended to the tag", mark)
|
||||||
|
if unsafe:
|
||||||
|
try:
|
||||||
|
__import__(name)
|
||||||
|
except ImportError as exc:
|
||||||
|
raise ConstructorError("while constructing a Python module", mark,
|
||||||
|
"cannot find module %r (%s)" % (name, exc), mark)
|
||||||
|
if name not in sys.modules:
|
||||||
|
raise ConstructorError("while constructing a Python module", mark,
|
||||||
|
"module %r is not imported" % name, mark)
|
||||||
|
return sys.modules[name]
|
||||||
|
|
||||||
|
def find_python_name(self, name, mark, unsafe=False):
|
||||||
|
if not name:
|
||||||
|
raise ConstructorError("while constructing a Python object", mark,
|
||||||
|
"expected non-empty name appended to the tag", mark)
|
||||||
|
if '.' in name:
|
||||||
|
module_name, object_name = name.rsplit('.', 1)
|
||||||
|
else:
|
||||||
|
module_name = 'builtins'
|
||||||
|
object_name = name
|
||||||
|
if unsafe:
|
||||||
|
try:
|
||||||
|
__import__(module_name)
|
||||||
|
except ImportError as exc:
|
||||||
|
raise ConstructorError("while constructing a Python object", mark,
|
||||||
|
"cannot find module %r (%s)" % (module_name, exc), mark)
|
||||||
|
if module_name not in sys.modules:
|
||||||
|
raise ConstructorError("while constructing a Python object", mark,
|
||||||
|
"module %r is not imported" % module_name, mark)
|
||||||
|
module = sys.modules[module_name]
|
||||||
|
if not hasattr(module, object_name):
|
||||||
|
raise ConstructorError("while constructing a Python object", mark,
|
||||||
|
"cannot find %r in the module %r"
|
||||||
|
% (object_name, module.__name__), mark)
|
||||||
|
return getattr(module, object_name)
|
||||||
|
|
||||||
|
def construct_python_name(self, suffix, node):
|
||||||
|
value = self.construct_scalar(node)
|
||||||
|
if value:
|
||||||
|
raise ConstructorError("while constructing a Python name", node.start_mark,
|
||||||
|
"expected the empty value, but found %r" % value, node.start_mark)
|
||||||
|
return self.find_python_name(suffix, node.start_mark)
|
||||||
|
|
||||||
|
def construct_python_module(self, suffix, node):
|
||||||
|
value = self.construct_scalar(node)
|
||||||
|
if value:
|
||||||
|
raise ConstructorError("while constructing a Python module", node.start_mark,
|
||||||
|
"expected the empty value, but found %r" % value, node.start_mark)
|
||||||
|
return self.find_python_module(suffix, node.start_mark)
|
||||||
|
|
||||||
|
def make_python_instance(self, suffix, node,
|
||||||
|
args=None, kwds=None, newobj=False, unsafe=False):
|
||||||
|
if not args:
|
||||||
|
args = []
|
||||||
|
if not kwds:
|
||||||
|
kwds = {}
|
||||||
|
cls = self.find_python_name(suffix, node.start_mark)
|
||||||
|
if not (unsafe or isinstance(cls, type)):
|
||||||
|
raise ConstructorError("while constructing a Python instance", node.start_mark,
|
||||||
|
"expected a class, but found %r" % type(cls),
|
||||||
|
node.start_mark)
|
||||||
|
if newobj and isinstance(cls, type):
|
||||||
|
return cls.__new__(cls, *args, **kwds)
|
||||||
|
else:
|
||||||
|
return cls(*args, **kwds)
|
||||||
|
|
||||||
|
def set_python_instance_state(self, instance, state, unsafe=False):
|
||||||
|
if hasattr(instance, '__setstate__'):
|
||||||
|
instance.__setstate__(state)
|
||||||
|
else:
|
||||||
|
slotstate = {}
|
||||||
|
if isinstance(state, tuple) and len(state) == 2:
|
||||||
|
state, slotstate = state
|
||||||
|
if hasattr(instance, '__dict__'):
|
||||||
|
if not unsafe and state:
|
||||||
|
for key in state.keys():
|
||||||
|
self.check_state_key(key)
|
||||||
|
instance.__dict__.update(state)
|
||||||
|
elif state:
|
||||||
|
slotstate.update(state)
|
||||||
|
for key, value in slotstate.items():
|
||||||
|
if not unsafe:
|
||||||
|
self.check_state_key(key)
|
||||||
|
setattr(instance, key, value)
|
||||||
|
|
||||||
|
def construct_python_object(self, suffix, node):
|
||||||
|
# Format:
|
||||||
|
# !!python/object:module.name { ... state ... }
|
||||||
|
instance = self.make_python_instance(suffix, node, newobj=True)
|
||||||
|
yield instance
|
||||||
|
deep = hasattr(instance, '__setstate__')
|
||||||
|
state = self.construct_mapping(node, deep=deep)
|
||||||
|
self.set_python_instance_state(instance, state)
|
||||||
|
|
||||||
|
def construct_python_object_apply(self, suffix, node, newobj=False):
|
||||||
|
# Format:
|
||||||
|
# !!python/object/apply # (or !!python/object/new)
|
||||||
|
# args: [ ... arguments ... ]
|
||||||
|
# kwds: { ... keywords ... }
|
||||||
|
# state: ... state ...
|
||||||
|
# listitems: [ ... listitems ... ]
|
||||||
|
# dictitems: { ... dictitems ... }
|
||||||
|
# or short format:
|
||||||
|
# !!python/object/apply [ ... arguments ... ]
|
||||||
|
# The difference between !!python/object/apply and !!python/object/new
|
||||||
|
# is how an object is created, check make_python_instance for details.
|
||||||
|
if isinstance(node, SequenceNode):
|
||||||
|
args = self.construct_sequence(node, deep=True)
|
||||||
|
kwds = {}
|
||||||
|
state = {}
|
||||||
|
listitems = []
|
||||||
|
dictitems = {}
|
||||||
|
else:
|
||||||
|
value = self.construct_mapping(node, deep=True)
|
||||||
|
args = value.get('args', [])
|
||||||
|
kwds = value.get('kwds', {})
|
||||||
|
state = value.get('state', {})
|
||||||
|
listitems = value.get('listitems', [])
|
||||||
|
dictitems = value.get('dictitems', {})
|
||||||
|
instance = self.make_python_instance(suffix, node, args, kwds, newobj)
|
||||||
|
if state:
|
||||||
|
self.set_python_instance_state(instance, state)
|
||||||
|
if listitems:
|
||||||
|
instance.extend(listitems)
|
||||||
|
if dictitems:
|
||||||
|
for key in dictitems:
|
||||||
|
instance[key] = dictitems[key]
|
||||||
|
return instance
|
||||||
|
|
||||||
|
def construct_python_object_new(self, suffix, node):
|
||||||
|
return self.construct_python_object_apply(suffix, node, newobj=True)
|
||||||
|
|
||||||
|
FullConstructor.add_constructor(
|
||||||
|
'tag:yaml.org,2002:python/none',
|
||||||
|
FullConstructor.construct_yaml_null)
|
||||||
|
|
||||||
|
FullConstructor.add_constructor(
|
||||||
|
'tag:yaml.org,2002:python/bool',
|
||||||
|
FullConstructor.construct_yaml_bool)
|
||||||
|
|
||||||
|
FullConstructor.add_constructor(
|
||||||
|
'tag:yaml.org,2002:python/str',
|
||||||
|
FullConstructor.construct_python_str)
|
||||||
|
|
||||||
|
FullConstructor.add_constructor(
|
||||||
|
'tag:yaml.org,2002:python/unicode',
|
||||||
|
FullConstructor.construct_python_unicode)
|
||||||
|
|
||||||
|
FullConstructor.add_constructor(
|
||||||
|
'tag:yaml.org,2002:python/bytes',
|
||||||
|
FullConstructor.construct_python_bytes)
|
||||||
|
|
||||||
|
FullConstructor.add_constructor(
|
||||||
|
'tag:yaml.org,2002:python/int',
|
||||||
|
FullConstructor.construct_yaml_int)
|
||||||
|
|
||||||
|
FullConstructor.add_constructor(
|
||||||
|
'tag:yaml.org,2002:python/long',
|
||||||
|
FullConstructor.construct_python_long)
|
||||||
|
|
||||||
|
FullConstructor.add_constructor(
|
||||||
|
'tag:yaml.org,2002:python/float',
|
||||||
|
FullConstructor.construct_yaml_float)
|
||||||
|
|
||||||
|
FullConstructor.add_constructor(
|
||||||
|
'tag:yaml.org,2002:python/complex',
|
||||||
|
FullConstructor.construct_python_complex)
|
||||||
|
|
||||||
|
FullConstructor.add_constructor(
|
||||||
|
'tag:yaml.org,2002:python/list',
|
||||||
|
FullConstructor.construct_yaml_seq)
|
||||||
|
|
||||||
|
FullConstructor.add_constructor(
|
||||||
|
'tag:yaml.org,2002:python/tuple',
|
||||||
|
FullConstructor.construct_python_tuple)
|
||||||
|
|
||||||
|
FullConstructor.add_constructor(
|
||||||
|
'tag:yaml.org,2002:python/dict',
|
||||||
|
FullConstructor.construct_yaml_map)
|
||||||
|
|
||||||
|
FullConstructor.add_multi_constructor(
|
||||||
|
'tag:yaml.org,2002:python/name:',
|
||||||
|
FullConstructor.construct_python_name)
|
||||||
|
|
||||||
|
class UnsafeConstructor(FullConstructor):
|
||||||
|
|
||||||
|
def find_python_module(self, name, mark):
|
||||||
|
return super(UnsafeConstructor, self).find_python_module(name, mark, unsafe=True)
|
||||||
|
|
||||||
|
def find_python_name(self, name, mark):
|
||||||
|
return super(UnsafeConstructor, self).find_python_name(name, mark, unsafe=True)
|
||||||
|
|
||||||
|
def make_python_instance(self, suffix, node, args=None, kwds=None, newobj=False):
|
||||||
|
return super(UnsafeConstructor, self).make_python_instance(
|
||||||
|
suffix, node, args, kwds, newobj, unsafe=True)
|
||||||
|
|
||||||
|
def set_python_instance_state(self, instance, state):
|
||||||
|
return super(UnsafeConstructor, self).set_python_instance_state(
|
||||||
|
instance, state, unsafe=True)
|
||||||
|
|
||||||
|
UnsafeConstructor.add_multi_constructor(
|
||||||
|
'tag:yaml.org,2002:python/module:',
|
||||||
|
UnsafeConstructor.construct_python_module)
|
||||||
|
|
||||||
|
UnsafeConstructor.add_multi_constructor(
|
||||||
|
'tag:yaml.org,2002:python/object:',
|
||||||
|
UnsafeConstructor.construct_python_object)
|
||||||
|
|
||||||
|
UnsafeConstructor.add_multi_constructor(
|
||||||
|
'tag:yaml.org,2002:python/object/new:',
|
||||||
|
UnsafeConstructor.construct_python_object_new)
|
||||||
|
|
||||||
|
UnsafeConstructor.add_multi_constructor(
|
||||||
|
'tag:yaml.org,2002:python/object/apply:',
|
||||||
|
UnsafeConstructor.construct_python_object_apply)
|
||||||
|
|
||||||
|
# Constructor is same as UnsafeConstructor. Need to leave this in place in case
|
||||||
|
# people have extended it directly.
|
||||||
|
class Constructor(UnsafeConstructor):
|
||||||
|
pass
|
||||||
101
lib/sunhpc/yaml/cyaml.py
Normal file
101
lib/sunhpc/yaml/cyaml.py
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
|
||||||
|
__all__ = [
|
||||||
|
'CBaseLoader', 'CSafeLoader', 'CFullLoader', 'CUnsafeLoader', 'CLoader',
|
||||||
|
'CBaseDumper', 'CSafeDumper', 'CDumper'
|
||||||
|
]
|
||||||
|
|
||||||
|
from yaml._yaml import CParser, CEmitter
|
||||||
|
|
||||||
|
from .constructor import *
|
||||||
|
|
||||||
|
from .serializer import *
|
||||||
|
from .representer import *
|
||||||
|
|
||||||
|
from .resolver import *
|
||||||
|
|
||||||
|
class CBaseLoader(CParser, BaseConstructor, BaseResolver):
|
||||||
|
|
||||||
|
def __init__(self, stream):
|
||||||
|
CParser.__init__(self, stream)
|
||||||
|
BaseConstructor.__init__(self)
|
||||||
|
BaseResolver.__init__(self)
|
||||||
|
|
||||||
|
class CSafeLoader(CParser, SafeConstructor, Resolver):
|
||||||
|
|
||||||
|
def __init__(self, stream):
|
||||||
|
CParser.__init__(self, stream)
|
||||||
|
SafeConstructor.__init__(self)
|
||||||
|
Resolver.__init__(self)
|
||||||
|
|
||||||
|
class CFullLoader(CParser, FullConstructor, Resolver):
|
||||||
|
|
||||||
|
def __init__(self, stream):
|
||||||
|
CParser.__init__(self, stream)
|
||||||
|
FullConstructor.__init__(self)
|
||||||
|
Resolver.__init__(self)
|
||||||
|
|
||||||
|
class CUnsafeLoader(CParser, UnsafeConstructor, Resolver):
|
||||||
|
|
||||||
|
def __init__(self, stream):
|
||||||
|
CParser.__init__(self, stream)
|
||||||
|
UnsafeConstructor.__init__(self)
|
||||||
|
Resolver.__init__(self)
|
||||||
|
|
||||||
|
class CLoader(CParser, Constructor, Resolver):
|
||||||
|
|
||||||
|
def __init__(self, stream):
|
||||||
|
CParser.__init__(self, stream)
|
||||||
|
Constructor.__init__(self)
|
||||||
|
Resolver.__init__(self)
|
||||||
|
|
||||||
|
class CBaseDumper(CEmitter, BaseRepresenter, BaseResolver):
|
||||||
|
|
||||||
|
def __init__(self, stream,
|
||||||
|
default_style=None, default_flow_style=False,
|
||||||
|
canonical=None, indent=None, width=None,
|
||||||
|
allow_unicode=None, line_break=None,
|
||||||
|
encoding=None, explicit_start=None, explicit_end=None,
|
||||||
|
version=None, tags=None, sort_keys=True):
|
||||||
|
CEmitter.__init__(self, stream, canonical=canonical,
|
||||||
|
indent=indent, width=width, encoding=encoding,
|
||||||
|
allow_unicode=allow_unicode, line_break=line_break,
|
||||||
|
explicit_start=explicit_start, explicit_end=explicit_end,
|
||||||
|
version=version, tags=tags)
|
||||||
|
Representer.__init__(self, default_style=default_style,
|
||||||
|
default_flow_style=default_flow_style, sort_keys=sort_keys)
|
||||||
|
Resolver.__init__(self)
|
||||||
|
|
||||||
|
class CSafeDumper(CEmitter, SafeRepresenter, Resolver):
|
||||||
|
|
||||||
|
def __init__(self, stream,
|
||||||
|
default_style=None, default_flow_style=False,
|
||||||
|
canonical=None, indent=None, width=None,
|
||||||
|
allow_unicode=None, line_break=None,
|
||||||
|
encoding=None, explicit_start=None, explicit_end=None,
|
||||||
|
version=None, tags=None, sort_keys=True):
|
||||||
|
CEmitter.__init__(self, stream, canonical=canonical,
|
||||||
|
indent=indent, width=width, encoding=encoding,
|
||||||
|
allow_unicode=allow_unicode, line_break=line_break,
|
||||||
|
explicit_start=explicit_start, explicit_end=explicit_end,
|
||||||
|
version=version, tags=tags)
|
||||||
|
SafeRepresenter.__init__(self, default_style=default_style,
|
||||||
|
default_flow_style=default_flow_style, sort_keys=sort_keys)
|
||||||
|
Resolver.__init__(self)
|
||||||
|
|
||||||
|
class CDumper(CEmitter, Serializer, Representer, Resolver):
|
||||||
|
|
||||||
|
def __init__(self, stream,
|
||||||
|
default_style=None, default_flow_style=False,
|
||||||
|
canonical=None, indent=None, width=None,
|
||||||
|
allow_unicode=None, line_break=None,
|
||||||
|
encoding=None, explicit_start=None, explicit_end=None,
|
||||||
|
version=None, tags=None, sort_keys=True):
|
||||||
|
CEmitter.__init__(self, stream, canonical=canonical,
|
||||||
|
indent=indent, width=width, encoding=encoding,
|
||||||
|
allow_unicode=allow_unicode, line_break=line_break,
|
||||||
|
explicit_start=explicit_start, explicit_end=explicit_end,
|
||||||
|
version=version, tags=tags)
|
||||||
|
Representer.__init__(self, default_style=default_style,
|
||||||
|
default_flow_style=default_flow_style, sort_keys=sort_keys)
|
||||||
|
Resolver.__init__(self)
|
||||||
|
|
||||||
62
lib/sunhpc/yaml/dumper.py
Normal file
62
lib/sunhpc/yaml/dumper.py
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
|
||||||
|
__all__ = ['BaseDumper', 'SafeDumper', 'Dumper']
|
||||||
|
|
||||||
|
from .emitter import *
|
||||||
|
from .serializer import *
|
||||||
|
from .representer import *
|
||||||
|
from .resolver import *
|
||||||
|
|
||||||
|
class BaseDumper(Emitter, Serializer, BaseRepresenter, BaseResolver):
|
||||||
|
|
||||||
|
def __init__(self, stream,
|
||||||
|
default_style=None, default_flow_style=False,
|
||||||
|
canonical=None, indent=None, width=None,
|
||||||
|
allow_unicode=None, line_break=None,
|
||||||
|
encoding=None, explicit_start=None, explicit_end=None,
|
||||||
|
version=None, tags=None, sort_keys=True):
|
||||||
|
Emitter.__init__(self, stream, canonical=canonical,
|
||||||
|
indent=indent, width=width,
|
||||||
|
allow_unicode=allow_unicode, line_break=line_break)
|
||||||
|
Serializer.__init__(self, encoding=encoding,
|
||||||
|
explicit_start=explicit_start, explicit_end=explicit_end,
|
||||||
|
version=version, tags=tags)
|
||||||
|
Representer.__init__(self, default_style=default_style,
|
||||||
|
default_flow_style=default_flow_style, sort_keys=sort_keys)
|
||||||
|
Resolver.__init__(self)
|
||||||
|
|
||||||
|
class SafeDumper(Emitter, Serializer, SafeRepresenter, Resolver):
|
||||||
|
|
||||||
|
def __init__(self, stream,
|
||||||
|
default_style=None, default_flow_style=False,
|
||||||
|
canonical=None, indent=None, width=None,
|
||||||
|
allow_unicode=None, line_break=None,
|
||||||
|
encoding=None, explicit_start=None, explicit_end=None,
|
||||||
|
version=None, tags=None, sort_keys=True):
|
||||||
|
Emitter.__init__(self, stream, canonical=canonical,
|
||||||
|
indent=indent, width=width,
|
||||||
|
allow_unicode=allow_unicode, line_break=line_break)
|
||||||
|
Serializer.__init__(self, encoding=encoding,
|
||||||
|
explicit_start=explicit_start, explicit_end=explicit_end,
|
||||||
|
version=version, tags=tags)
|
||||||
|
SafeRepresenter.__init__(self, default_style=default_style,
|
||||||
|
default_flow_style=default_flow_style, sort_keys=sort_keys)
|
||||||
|
Resolver.__init__(self)
|
||||||
|
|
||||||
|
class Dumper(Emitter, Serializer, Representer, Resolver):
|
||||||
|
|
||||||
|
def __init__(self, stream,
|
||||||
|
default_style=None, default_flow_style=False,
|
||||||
|
canonical=None, indent=None, width=None,
|
||||||
|
allow_unicode=None, line_break=None,
|
||||||
|
encoding=None, explicit_start=None, explicit_end=None,
|
||||||
|
version=None, tags=None, sort_keys=True):
|
||||||
|
Emitter.__init__(self, stream, canonical=canonical,
|
||||||
|
indent=indent, width=width,
|
||||||
|
allow_unicode=allow_unicode, line_break=line_break)
|
||||||
|
Serializer.__init__(self, encoding=encoding,
|
||||||
|
explicit_start=explicit_start, explicit_end=explicit_end,
|
||||||
|
version=version, tags=tags)
|
||||||
|
Representer.__init__(self, default_style=default_style,
|
||||||
|
default_flow_style=default_flow_style, sort_keys=sort_keys)
|
||||||
|
Resolver.__init__(self)
|
||||||
|
|
||||||
1137
lib/sunhpc/yaml/emitter.py
Normal file
1137
lib/sunhpc/yaml/emitter.py
Normal file
File diff suppressed because it is too large
Load Diff
75
lib/sunhpc/yaml/error.py
Normal file
75
lib/sunhpc/yaml/error.py
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
|
||||||
|
__all__ = ['Mark', 'YAMLError', 'MarkedYAMLError']
|
||||||
|
|
||||||
|
class Mark:
|
||||||
|
|
||||||
|
def __init__(self, name, index, line, column, buffer, pointer):
|
||||||
|
self.name = name
|
||||||
|
self.index = index
|
||||||
|
self.line = line
|
||||||
|
self.column = column
|
||||||
|
self.buffer = buffer
|
||||||
|
self.pointer = pointer
|
||||||
|
|
||||||
|
def get_snippet(self, indent=4, max_length=75):
|
||||||
|
if self.buffer is None:
|
||||||
|
return None
|
||||||
|
head = ''
|
||||||
|
start = self.pointer
|
||||||
|
while start > 0 and self.buffer[start-1] not in '\0\r\n\x85\u2028\u2029':
|
||||||
|
start -= 1
|
||||||
|
if self.pointer-start > max_length/2-1:
|
||||||
|
head = ' ... '
|
||||||
|
start += 5
|
||||||
|
break
|
||||||
|
tail = ''
|
||||||
|
end = self.pointer
|
||||||
|
while end < len(self.buffer) and self.buffer[end] not in '\0\r\n\x85\u2028\u2029':
|
||||||
|
end += 1
|
||||||
|
if end-self.pointer > max_length/2-1:
|
||||||
|
tail = ' ... '
|
||||||
|
end -= 5
|
||||||
|
break
|
||||||
|
snippet = self.buffer[start:end]
|
||||||
|
return ' '*indent + head + snippet + tail + '\n' \
|
||||||
|
+ ' '*(indent+self.pointer-start+len(head)) + '^'
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
snippet = self.get_snippet()
|
||||||
|
where = " in \"%s\", line %d, column %d" \
|
||||||
|
% (self.name, self.line+1, self.column+1)
|
||||||
|
if snippet is not None:
|
||||||
|
where += ":\n"+snippet
|
||||||
|
return where
|
||||||
|
|
||||||
|
class YAMLError(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class MarkedYAMLError(YAMLError):
|
||||||
|
|
||||||
|
def __init__(self, context=None, context_mark=None,
|
||||||
|
problem=None, problem_mark=None, note=None):
|
||||||
|
self.context = context
|
||||||
|
self.context_mark = context_mark
|
||||||
|
self.problem = problem
|
||||||
|
self.problem_mark = problem_mark
|
||||||
|
self.note = note
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
lines = []
|
||||||
|
if self.context is not None:
|
||||||
|
lines.append(self.context)
|
||||||
|
if self.context_mark is not None \
|
||||||
|
and (self.problem is None or self.problem_mark is None
|
||||||
|
or self.context_mark.name != self.problem_mark.name
|
||||||
|
or self.context_mark.line != self.problem_mark.line
|
||||||
|
or self.context_mark.column != self.problem_mark.column):
|
||||||
|
lines.append(str(self.context_mark))
|
||||||
|
if self.problem is not None:
|
||||||
|
lines.append(self.problem)
|
||||||
|
if self.problem_mark is not None:
|
||||||
|
lines.append(str(self.problem_mark))
|
||||||
|
if self.note is not None:
|
||||||
|
lines.append(self.note)
|
||||||
|
return '\n'.join(lines)
|
||||||
|
|
||||||
86
lib/sunhpc/yaml/events.py
Normal file
86
lib/sunhpc/yaml/events.py
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
|
||||||
|
# Abstract classes.
|
||||||
|
|
||||||
|
class Event(object):
|
||||||
|
def __init__(self, start_mark=None, end_mark=None):
|
||||||
|
self.start_mark = start_mark
|
||||||
|
self.end_mark = end_mark
|
||||||
|
def __repr__(self):
|
||||||
|
attributes = [key for key in ['anchor', 'tag', 'implicit', 'value']
|
||||||
|
if hasattr(self, key)]
|
||||||
|
arguments = ', '.join(['%s=%r' % (key, getattr(self, key))
|
||||||
|
for key in attributes])
|
||||||
|
return '%s(%s)' % (self.__class__.__name__, arguments)
|
||||||
|
|
||||||
|
class NodeEvent(Event):
|
||||||
|
def __init__(self, anchor, start_mark=None, end_mark=None):
|
||||||
|
self.anchor = anchor
|
||||||
|
self.start_mark = start_mark
|
||||||
|
self.end_mark = end_mark
|
||||||
|
|
||||||
|
class CollectionStartEvent(NodeEvent):
|
||||||
|
def __init__(self, anchor, tag, implicit, start_mark=None, end_mark=None,
|
||||||
|
flow_style=None):
|
||||||
|
self.anchor = anchor
|
||||||
|
self.tag = tag
|
||||||
|
self.implicit = implicit
|
||||||
|
self.start_mark = start_mark
|
||||||
|
self.end_mark = end_mark
|
||||||
|
self.flow_style = flow_style
|
||||||
|
|
||||||
|
class CollectionEndEvent(Event):
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Implementations.
|
||||||
|
|
||||||
|
class StreamStartEvent(Event):
|
||||||
|
def __init__(self, start_mark=None, end_mark=None, encoding=None):
|
||||||
|
self.start_mark = start_mark
|
||||||
|
self.end_mark = end_mark
|
||||||
|
self.encoding = encoding
|
||||||
|
|
||||||
|
class StreamEndEvent(Event):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class DocumentStartEvent(Event):
|
||||||
|
def __init__(self, start_mark=None, end_mark=None,
|
||||||
|
explicit=None, version=None, tags=None):
|
||||||
|
self.start_mark = start_mark
|
||||||
|
self.end_mark = end_mark
|
||||||
|
self.explicit = explicit
|
||||||
|
self.version = version
|
||||||
|
self.tags = tags
|
||||||
|
|
||||||
|
class DocumentEndEvent(Event):
|
||||||
|
def __init__(self, start_mark=None, end_mark=None,
|
||||||
|
explicit=None):
|
||||||
|
self.start_mark = start_mark
|
||||||
|
self.end_mark = end_mark
|
||||||
|
self.explicit = explicit
|
||||||
|
|
||||||
|
class AliasEvent(NodeEvent):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class ScalarEvent(NodeEvent):
|
||||||
|
def __init__(self, anchor, tag, implicit, value,
|
||||||
|
start_mark=None, end_mark=None, style=None):
|
||||||
|
self.anchor = anchor
|
||||||
|
self.tag = tag
|
||||||
|
self.implicit = implicit
|
||||||
|
self.value = value
|
||||||
|
self.start_mark = start_mark
|
||||||
|
self.end_mark = end_mark
|
||||||
|
self.style = style
|
||||||
|
|
||||||
|
class SequenceStartEvent(CollectionStartEvent):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class SequenceEndEvent(CollectionEndEvent):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class MappingStartEvent(CollectionStartEvent):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class MappingEndEvent(CollectionEndEvent):
|
||||||
|
pass
|
||||||
|
|
||||||
63
lib/sunhpc/yaml/loader.py
Normal file
63
lib/sunhpc/yaml/loader.py
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
|
||||||
|
__all__ = ['BaseLoader', 'FullLoader', 'SafeLoader', 'Loader', 'UnsafeLoader']
|
||||||
|
|
||||||
|
from .reader import *
|
||||||
|
from .scanner import *
|
||||||
|
from .parser import *
|
||||||
|
from .composer import *
|
||||||
|
from .constructor import *
|
||||||
|
from .resolver import *
|
||||||
|
|
||||||
|
class BaseLoader(Reader, Scanner, Parser, Composer, BaseConstructor, BaseResolver):
|
||||||
|
|
||||||
|
def __init__(self, stream):
|
||||||
|
Reader.__init__(self, stream)
|
||||||
|
Scanner.__init__(self)
|
||||||
|
Parser.__init__(self)
|
||||||
|
Composer.__init__(self)
|
||||||
|
BaseConstructor.__init__(self)
|
||||||
|
BaseResolver.__init__(self)
|
||||||
|
|
||||||
|
class FullLoader(Reader, Scanner, Parser, Composer, FullConstructor, Resolver):
|
||||||
|
|
||||||
|
def __init__(self, stream):
|
||||||
|
Reader.__init__(self, stream)
|
||||||
|
Scanner.__init__(self)
|
||||||
|
Parser.__init__(self)
|
||||||
|
Composer.__init__(self)
|
||||||
|
FullConstructor.__init__(self)
|
||||||
|
Resolver.__init__(self)
|
||||||
|
|
||||||
|
class SafeLoader(Reader, Scanner, Parser, Composer, SafeConstructor, Resolver):
|
||||||
|
|
||||||
|
def __init__(self, stream):
|
||||||
|
Reader.__init__(self, stream)
|
||||||
|
Scanner.__init__(self)
|
||||||
|
Parser.__init__(self)
|
||||||
|
Composer.__init__(self)
|
||||||
|
SafeConstructor.__init__(self)
|
||||||
|
Resolver.__init__(self)
|
||||||
|
|
||||||
|
class Loader(Reader, Scanner, Parser, Composer, Constructor, Resolver):
|
||||||
|
|
||||||
|
def __init__(self, stream):
|
||||||
|
Reader.__init__(self, stream)
|
||||||
|
Scanner.__init__(self)
|
||||||
|
Parser.__init__(self)
|
||||||
|
Composer.__init__(self)
|
||||||
|
Constructor.__init__(self)
|
||||||
|
Resolver.__init__(self)
|
||||||
|
|
||||||
|
# UnsafeLoader is the same as Loader (which is and was always unsafe on
|
||||||
|
# untrusted input). Use of either Loader or UnsafeLoader should be rare, since
|
||||||
|
# FullLoad should be able to load almost all YAML safely. Loader is left intact
|
||||||
|
# to ensure backwards compatibility.
|
||||||
|
class UnsafeLoader(Reader, Scanner, Parser, Composer, Constructor, Resolver):
|
||||||
|
|
||||||
|
def __init__(self, stream):
|
||||||
|
Reader.__init__(self, stream)
|
||||||
|
Scanner.__init__(self)
|
||||||
|
Parser.__init__(self)
|
||||||
|
Composer.__init__(self)
|
||||||
|
Constructor.__init__(self)
|
||||||
|
Resolver.__init__(self)
|
||||||
49
lib/sunhpc/yaml/nodes.py
Normal file
49
lib/sunhpc/yaml/nodes.py
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
|
||||||
|
class Node(object):
|
||||||
|
def __init__(self, tag, value, start_mark, end_mark):
|
||||||
|
self.tag = tag
|
||||||
|
self.value = value
|
||||||
|
self.start_mark = start_mark
|
||||||
|
self.end_mark = end_mark
|
||||||
|
def __repr__(self):
|
||||||
|
value = self.value
|
||||||
|
#if isinstance(value, list):
|
||||||
|
# if len(value) == 0:
|
||||||
|
# value = '<empty>'
|
||||||
|
# elif len(value) == 1:
|
||||||
|
# value = '<1 item>'
|
||||||
|
# else:
|
||||||
|
# value = '<%d items>' % len(value)
|
||||||
|
#else:
|
||||||
|
# if len(value) > 75:
|
||||||
|
# value = repr(value[:70]+u' ... ')
|
||||||
|
# else:
|
||||||
|
# value = repr(value)
|
||||||
|
value = repr(value)
|
||||||
|
return '%s(tag=%r, value=%s)' % (self.__class__.__name__, self.tag, value)
|
||||||
|
|
||||||
|
class ScalarNode(Node):
|
||||||
|
id = 'scalar'
|
||||||
|
def __init__(self, tag, value,
|
||||||
|
start_mark=None, end_mark=None, style=None):
|
||||||
|
self.tag = tag
|
||||||
|
self.value = value
|
||||||
|
self.start_mark = start_mark
|
||||||
|
self.end_mark = end_mark
|
||||||
|
self.style = style
|
||||||
|
|
||||||
|
class CollectionNode(Node):
|
||||||
|
def __init__(self, tag, value,
|
||||||
|
start_mark=None, end_mark=None, flow_style=None):
|
||||||
|
self.tag = tag
|
||||||
|
self.value = value
|
||||||
|
self.start_mark = start_mark
|
||||||
|
self.end_mark = end_mark
|
||||||
|
self.flow_style = flow_style
|
||||||
|
|
||||||
|
class SequenceNode(CollectionNode):
|
||||||
|
id = 'sequence'
|
||||||
|
|
||||||
|
class MappingNode(CollectionNode):
|
||||||
|
id = 'mapping'
|
||||||
|
|
||||||
589
lib/sunhpc/yaml/parser.py
Normal file
589
lib/sunhpc/yaml/parser.py
Normal file
@@ -0,0 +1,589 @@
|
|||||||
|
|
||||||
|
# The following YAML grammar is LL(1) and is parsed by a recursive descent
|
||||||
|
# parser.
|
||||||
|
#
|
||||||
|
# stream ::= STREAM-START implicit_document? explicit_document* STREAM-END
|
||||||
|
# implicit_document ::= block_node DOCUMENT-END*
|
||||||
|
# explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END*
|
||||||
|
# block_node_or_indentless_sequence ::=
|
||||||
|
# ALIAS
|
||||||
|
# | properties (block_content | indentless_block_sequence)?
|
||||||
|
# | block_content
|
||||||
|
# | indentless_block_sequence
|
||||||
|
# block_node ::= ALIAS
|
||||||
|
# | properties block_content?
|
||||||
|
# | block_content
|
||||||
|
# flow_node ::= ALIAS
|
||||||
|
# | properties flow_content?
|
||||||
|
# | flow_content
|
||||||
|
# properties ::= TAG ANCHOR? | ANCHOR TAG?
|
||||||
|
# block_content ::= block_collection | flow_collection | SCALAR
|
||||||
|
# flow_content ::= flow_collection | SCALAR
|
||||||
|
# block_collection ::= block_sequence | block_mapping
|
||||||
|
# flow_collection ::= flow_sequence | flow_mapping
|
||||||
|
# block_sequence ::= BLOCK-SEQUENCE-START (BLOCK-ENTRY block_node?)* BLOCK-END
|
||||||
|
# indentless_sequence ::= (BLOCK-ENTRY block_node?)+
|
||||||
|
# block_mapping ::= BLOCK-MAPPING_START
|
||||||
|
# ((KEY block_node_or_indentless_sequence?)?
|
||||||
|
# (VALUE block_node_or_indentless_sequence?)?)*
|
||||||
|
# BLOCK-END
|
||||||
|
# flow_sequence ::= FLOW-SEQUENCE-START
|
||||||
|
# (flow_sequence_entry FLOW-ENTRY)*
|
||||||
|
# flow_sequence_entry?
|
||||||
|
# FLOW-SEQUENCE-END
|
||||||
|
# flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)?
|
||||||
|
# flow_mapping ::= FLOW-MAPPING-START
|
||||||
|
# (flow_mapping_entry FLOW-ENTRY)*
|
||||||
|
# flow_mapping_entry?
|
||||||
|
# FLOW-MAPPING-END
|
||||||
|
# flow_mapping_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)?
|
||||||
|
#
|
||||||
|
# FIRST sets:
|
||||||
|
#
|
||||||
|
# stream: { STREAM-START }
|
||||||
|
# explicit_document: { DIRECTIVE DOCUMENT-START }
|
||||||
|
# implicit_document: FIRST(block_node)
|
||||||
|
# block_node: { ALIAS TAG ANCHOR SCALAR BLOCK-SEQUENCE-START BLOCK-MAPPING-START FLOW-SEQUENCE-START FLOW-MAPPING-START }
|
||||||
|
# flow_node: { ALIAS ANCHOR TAG SCALAR FLOW-SEQUENCE-START FLOW-MAPPING-START }
|
||||||
|
# block_content: { BLOCK-SEQUENCE-START BLOCK-MAPPING-START FLOW-SEQUENCE-START FLOW-MAPPING-START SCALAR }
|
||||||
|
# flow_content: { FLOW-SEQUENCE-START FLOW-MAPPING-START SCALAR }
|
||||||
|
# block_collection: { BLOCK-SEQUENCE-START BLOCK-MAPPING-START }
|
||||||
|
# flow_collection: { FLOW-SEQUENCE-START FLOW-MAPPING-START }
|
||||||
|
# block_sequence: { BLOCK-SEQUENCE-START }
|
||||||
|
# block_mapping: { BLOCK-MAPPING-START }
|
||||||
|
# block_node_or_indentless_sequence: { ALIAS ANCHOR TAG SCALAR BLOCK-SEQUENCE-START BLOCK-MAPPING-START FLOW-SEQUENCE-START FLOW-MAPPING-START BLOCK-ENTRY }
|
||||||
|
# indentless_sequence: { ENTRY }
|
||||||
|
# flow_collection: { FLOW-SEQUENCE-START FLOW-MAPPING-START }
|
||||||
|
# flow_sequence: { FLOW-SEQUENCE-START }
|
||||||
|
# flow_mapping: { FLOW-MAPPING-START }
|
||||||
|
# flow_sequence_entry: { ALIAS ANCHOR TAG SCALAR FLOW-SEQUENCE-START FLOW-MAPPING-START KEY }
|
||||||
|
# flow_mapping_entry: { ALIAS ANCHOR TAG SCALAR FLOW-SEQUENCE-START FLOW-MAPPING-START KEY }
|
||||||
|
|
||||||
|
__all__ = ['Parser', 'ParserError']
|
||||||
|
|
||||||
|
from .error import MarkedYAMLError
|
||||||
|
from .tokens import *
|
||||||
|
from .events import *
|
||||||
|
from .scanner import *
|
||||||
|
|
||||||
|
class ParserError(MarkedYAMLError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class Parser:
|
||||||
|
# Since writing a recursive-descendant parser is a straightforward task, we
|
||||||
|
# do not give many comments here.
|
||||||
|
|
||||||
|
DEFAULT_TAGS = {
|
||||||
|
'!': '!',
|
||||||
|
'!!': 'tag:yaml.org,2002:',
|
||||||
|
}
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.current_event = None
|
||||||
|
self.yaml_version = None
|
||||||
|
self.tag_handles = {}
|
||||||
|
self.states = []
|
||||||
|
self.marks = []
|
||||||
|
self.state = self.parse_stream_start
|
||||||
|
|
||||||
|
def dispose(self):
|
||||||
|
# Reset the state attributes (to clear self-references)
|
||||||
|
self.states = []
|
||||||
|
self.state = None
|
||||||
|
|
||||||
|
def check_event(self, *choices):
|
||||||
|
# Check the type of the next event.
|
||||||
|
if self.current_event is None:
|
||||||
|
if self.state:
|
||||||
|
self.current_event = self.state()
|
||||||
|
if self.current_event is not None:
|
||||||
|
if not choices:
|
||||||
|
return True
|
||||||
|
for choice in choices:
|
||||||
|
if isinstance(self.current_event, choice):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def peek_event(self):
|
||||||
|
# Get the next event.
|
||||||
|
if self.current_event is None:
|
||||||
|
if self.state:
|
||||||
|
self.current_event = self.state()
|
||||||
|
return self.current_event
|
||||||
|
|
||||||
|
def get_event(self):
|
||||||
|
# Get the next event and proceed further.
|
||||||
|
if self.current_event is None:
|
||||||
|
if self.state:
|
||||||
|
self.current_event = self.state()
|
||||||
|
value = self.current_event
|
||||||
|
self.current_event = None
|
||||||
|
return value
|
||||||
|
|
||||||
|
# stream ::= STREAM-START implicit_document? explicit_document* STREAM-END
|
||||||
|
# implicit_document ::= block_node DOCUMENT-END*
|
||||||
|
# explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END*
|
||||||
|
|
||||||
|
def parse_stream_start(self):
|
||||||
|
|
||||||
|
# Parse the stream start.
|
||||||
|
token = self.get_token()
|
||||||
|
event = StreamStartEvent(token.start_mark, token.end_mark,
|
||||||
|
encoding=token.encoding)
|
||||||
|
|
||||||
|
# Prepare the next state.
|
||||||
|
self.state = self.parse_implicit_document_start
|
||||||
|
|
||||||
|
return event
|
||||||
|
|
||||||
|
def parse_implicit_document_start(self):
|
||||||
|
|
||||||
|
# Parse an implicit document.
|
||||||
|
if not self.check_token(DirectiveToken, DocumentStartToken,
|
||||||
|
StreamEndToken):
|
||||||
|
self.tag_handles = self.DEFAULT_TAGS
|
||||||
|
token = self.peek_token()
|
||||||
|
start_mark = end_mark = token.start_mark
|
||||||
|
event = DocumentStartEvent(start_mark, end_mark,
|
||||||
|
explicit=False)
|
||||||
|
|
||||||
|
# Prepare the next state.
|
||||||
|
self.states.append(self.parse_document_end)
|
||||||
|
self.state = self.parse_block_node
|
||||||
|
|
||||||
|
return event
|
||||||
|
|
||||||
|
else:
|
||||||
|
return self.parse_document_start()
|
||||||
|
|
||||||
|
def parse_document_start(self):
|
||||||
|
|
||||||
|
# Parse any extra document end indicators.
|
||||||
|
while self.check_token(DocumentEndToken):
|
||||||
|
self.get_token()
|
||||||
|
|
||||||
|
# Parse an explicit document.
|
||||||
|
if not self.check_token(StreamEndToken):
|
||||||
|
token = self.peek_token()
|
||||||
|
start_mark = token.start_mark
|
||||||
|
version, tags = self.process_directives()
|
||||||
|
if not self.check_token(DocumentStartToken):
|
||||||
|
raise ParserError(None, None,
|
||||||
|
"expected '<document start>', but found %r"
|
||||||
|
% self.peek_token().id,
|
||||||
|
self.peek_token().start_mark)
|
||||||
|
token = self.get_token()
|
||||||
|
end_mark = token.end_mark
|
||||||
|
event = DocumentStartEvent(start_mark, end_mark,
|
||||||
|
explicit=True, version=version, tags=tags)
|
||||||
|
self.states.append(self.parse_document_end)
|
||||||
|
self.state = self.parse_document_content
|
||||||
|
else:
|
||||||
|
# Parse the end of the stream.
|
||||||
|
token = self.get_token()
|
||||||
|
event = StreamEndEvent(token.start_mark, token.end_mark)
|
||||||
|
assert not self.states
|
||||||
|
assert not self.marks
|
||||||
|
self.state = None
|
||||||
|
return event
|
||||||
|
|
||||||
|
def parse_document_end(self):
|
||||||
|
|
||||||
|
# Parse the document end.
|
||||||
|
token = self.peek_token()
|
||||||
|
start_mark = end_mark = token.start_mark
|
||||||
|
explicit = False
|
||||||
|
if self.check_token(DocumentEndToken):
|
||||||
|
token = self.get_token()
|
||||||
|
end_mark = token.end_mark
|
||||||
|
explicit = True
|
||||||
|
event = DocumentEndEvent(start_mark, end_mark,
|
||||||
|
explicit=explicit)
|
||||||
|
|
||||||
|
# Prepare the next state.
|
||||||
|
self.state = self.parse_document_start
|
||||||
|
|
||||||
|
return event
|
||||||
|
|
||||||
|
def parse_document_content(self):
|
||||||
|
if self.check_token(DirectiveToken,
|
||||||
|
DocumentStartToken, DocumentEndToken, StreamEndToken):
|
||||||
|
event = self.process_empty_scalar(self.peek_token().start_mark)
|
||||||
|
self.state = self.states.pop()
|
||||||
|
return event
|
||||||
|
else:
|
||||||
|
return self.parse_block_node()
|
||||||
|
|
||||||
|
def process_directives(self):
|
||||||
|
self.yaml_version = None
|
||||||
|
self.tag_handles = {}
|
||||||
|
while self.check_token(DirectiveToken):
|
||||||
|
token = self.get_token()
|
||||||
|
if token.name == 'YAML':
|
||||||
|
if self.yaml_version is not None:
|
||||||
|
raise ParserError(None, None,
|
||||||
|
"found duplicate YAML directive", token.start_mark)
|
||||||
|
major, minor = token.value
|
||||||
|
if major != 1:
|
||||||
|
raise ParserError(None, None,
|
||||||
|
"found incompatible YAML document (version 1.* is required)",
|
||||||
|
token.start_mark)
|
||||||
|
self.yaml_version = token.value
|
||||||
|
elif token.name == 'TAG':
|
||||||
|
handle, prefix = token.value
|
||||||
|
if handle in self.tag_handles:
|
||||||
|
raise ParserError(None, None,
|
||||||
|
"duplicate tag handle %r" % handle,
|
||||||
|
token.start_mark)
|
||||||
|
self.tag_handles[handle] = prefix
|
||||||
|
if self.tag_handles:
|
||||||
|
value = self.yaml_version, self.tag_handles.copy()
|
||||||
|
else:
|
||||||
|
value = self.yaml_version, None
|
||||||
|
for key in self.DEFAULT_TAGS:
|
||||||
|
if key not in self.tag_handles:
|
||||||
|
self.tag_handles[key] = self.DEFAULT_TAGS[key]
|
||||||
|
return value
|
||||||
|
|
||||||
|
# block_node_or_indentless_sequence ::= ALIAS
|
||||||
|
# | properties (block_content | indentless_block_sequence)?
|
||||||
|
# | block_content
|
||||||
|
# | indentless_block_sequence
|
||||||
|
# block_node ::= ALIAS
|
||||||
|
# | properties block_content?
|
||||||
|
# | block_content
|
||||||
|
# flow_node ::= ALIAS
|
||||||
|
# | properties flow_content?
|
||||||
|
# | flow_content
|
||||||
|
# properties ::= TAG ANCHOR? | ANCHOR TAG?
|
||||||
|
# block_content ::= block_collection | flow_collection | SCALAR
|
||||||
|
# flow_content ::= flow_collection | SCALAR
|
||||||
|
# block_collection ::= block_sequence | block_mapping
|
||||||
|
# flow_collection ::= flow_sequence | flow_mapping
|
||||||
|
|
||||||
|
def parse_block_node(self):
|
||||||
|
return self.parse_node(block=True)
|
||||||
|
|
||||||
|
def parse_flow_node(self):
|
||||||
|
return self.parse_node()
|
||||||
|
|
||||||
|
def parse_block_node_or_indentless_sequence(self):
|
||||||
|
return self.parse_node(block=True, indentless_sequence=True)
|
||||||
|
|
||||||
|
def parse_node(self, block=False, indentless_sequence=False):
|
||||||
|
if self.check_token(AliasToken):
|
||||||
|
token = self.get_token()
|
||||||
|
event = AliasEvent(token.value, token.start_mark, token.end_mark)
|
||||||
|
self.state = self.states.pop()
|
||||||
|
else:
|
||||||
|
anchor = None
|
||||||
|
tag = None
|
||||||
|
start_mark = end_mark = tag_mark = None
|
||||||
|
if self.check_token(AnchorToken):
|
||||||
|
token = self.get_token()
|
||||||
|
start_mark = token.start_mark
|
||||||
|
end_mark = token.end_mark
|
||||||
|
anchor = token.value
|
||||||
|
if self.check_token(TagToken):
|
||||||
|
token = self.get_token()
|
||||||
|
tag_mark = token.start_mark
|
||||||
|
end_mark = token.end_mark
|
||||||
|
tag = token.value
|
||||||
|
elif self.check_token(TagToken):
|
||||||
|
token = self.get_token()
|
||||||
|
start_mark = tag_mark = token.start_mark
|
||||||
|
end_mark = token.end_mark
|
||||||
|
tag = token.value
|
||||||
|
if self.check_token(AnchorToken):
|
||||||
|
token = self.get_token()
|
||||||
|
end_mark = token.end_mark
|
||||||
|
anchor = token.value
|
||||||
|
if tag is not None:
|
||||||
|
handle, suffix = tag
|
||||||
|
if handle is not None:
|
||||||
|
if handle not in self.tag_handles:
|
||||||
|
raise ParserError("while parsing a node", start_mark,
|
||||||
|
"found undefined tag handle %r" % handle,
|
||||||
|
tag_mark)
|
||||||
|
tag = self.tag_handles[handle]+suffix
|
||||||
|
else:
|
||||||
|
tag = suffix
|
||||||
|
#if tag == '!':
|
||||||
|
# raise ParserError("while parsing a node", start_mark,
|
||||||
|
# "found non-specific tag '!'", tag_mark,
|
||||||
|
# "Please check 'http://pyyaml.org/wiki/YAMLNonSpecificTag' and share your opinion.")
|
||||||
|
if start_mark is None:
|
||||||
|
start_mark = end_mark = self.peek_token().start_mark
|
||||||
|
event = None
|
||||||
|
implicit = (tag is None or tag == '!')
|
||||||
|
if indentless_sequence and self.check_token(BlockEntryToken):
|
||||||
|
end_mark = self.peek_token().end_mark
|
||||||
|
event = SequenceStartEvent(anchor, tag, implicit,
|
||||||
|
start_mark, end_mark)
|
||||||
|
self.state = self.parse_indentless_sequence_entry
|
||||||
|
else:
|
||||||
|
if self.check_token(ScalarToken):
|
||||||
|
token = self.get_token()
|
||||||
|
end_mark = token.end_mark
|
||||||
|
if (token.plain and tag is None) or tag == '!':
|
||||||
|
implicit = (True, False)
|
||||||
|
elif tag is None:
|
||||||
|
implicit = (False, True)
|
||||||
|
else:
|
||||||
|
implicit = (False, False)
|
||||||
|
event = ScalarEvent(anchor, tag, implicit, token.value,
|
||||||
|
start_mark, end_mark, style=token.style)
|
||||||
|
self.state = self.states.pop()
|
||||||
|
elif self.check_token(FlowSequenceStartToken):
|
||||||
|
end_mark = self.peek_token().end_mark
|
||||||
|
event = SequenceStartEvent(anchor, tag, implicit,
|
||||||
|
start_mark, end_mark, flow_style=True)
|
||||||
|
self.state = self.parse_flow_sequence_first_entry
|
||||||
|
elif self.check_token(FlowMappingStartToken):
|
||||||
|
end_mark = self.peek_token().end_mark
|
||||||
|
event = MappingStartEvent(anchor, tag, implicit,
|
||||||
|
start_mark, end_mark, flow_style=True)
|
||||||
|
self.state = self.parse_flow_mapping_first_key
|
||||||
|
elif block and self.check_token(BlockSequenceStartToken):
|
||||||
|
end_mark = self.peek_token().start_mark
|
||||||
|
event = SequenceStartEvent(anchor, tag, implicit,
|
||||||
|
start_mark, end_mark, flow_style=False)
|
||||||
|
self.state = self.parse_block_sequence_first_entry
|
||||||
|
elif block and self.check_token(BlockMappingStartToken):
|
||||||
|
end_mark = self.peek_token().start_mark
|
||||||
|
event = MappingStartEvent(anchor, tag, implicit,
|
||||||
|
start_mark, end_mark, flow_style=False)
|
||||||
|
self.state = self.parse_block_mapping_first_key
|
||||||
|
elif anchor is not None or tag is not None:
|
||||||
|
# Empty scalars are allowed even if a tag or an anchor is
|
||||||
|
# specified.
|
||||||
|
event = ScalarEvent(anchor, tag, (implicit, False), '',
|
||||||
|
start_mark, end_mark)
|
||||||
|
self.state = self.states.pop()
|
||||||
|
else:
|
||||||
|
if block:
|
||||||
|
node = 'block'
|
||||||
|
else:
|
||||||
|
node = 'flow'
|
||||||
|
token = self.peek_token()
|
||||||
|
raise ParserError("while parsing a %s node" % node, start_mark,
|
||||||
|
"expected the node content, but found %r" % token.id,
|
||||||
|
token.start_mark)
|
||||||
|
return event
|
||||||
|
|
||||||
|
# block_sequence ::= BLOCK-SEQUENCE-START (BLOCK-ENTRY block_node?)* BLOCK-END
|
||||||
|
|
||||||
|
def parse_block_sequence_first_entry(self):
|
||||||
|
token = self.get_token()
|
||||||
|
self.marks.append(token.start_mark)
|
||||||
|
return self.parse_block_sequence_entry()
|
||||||
|
|
||||||
|
def parse_block_sequence_entry(self):
|
||||||
|
if self.check_token(BlockEntryToken):
|
||||||
|
token = self.get_token()
|
||||||
|
if not self.check_token(BlockEntryToken, BlockEndToken):
|
||||||
|
self.states.append(self.parse_block_sequence_entry)
|
||||||
|
return self.parse_block_node()
|
||||||
|
else:
|
||||||
|
self.state = self.parse_block_sequence_entry
|
||||||
|
return self.process_empty_scalar(token.end_mark)
|
||||||
|
if not self.check_token(BlockEndToken):
|
||||||
|
token = self.peek_token()
|
||||||
|
raise ParserError("while parsing a block collection", self.marks[-1],
|
||||||
|
"expected <block end>, but found %r" % token.id, token.start_mark)
|
||||||
|
token = self.get_token()
|
||||||
|
event = SequenceEndEvent(token.start_mark, token.end_mark)
|
||||||
|
self.state = self.states.pop()
|
||||||
|
self.marks.pop()
|
||||||
|
return event
|
||||||
|
|
||||||
|
# indentless_sequence ::= (BLOCK-ENTRY block_node?)+
|
||||||
|
|
||||||
|
def parse_indentless_sequence_entry(self):
|
||||||
|
if self.check_token(BlockEntryToken):
|
||||||
|
token = self.get_token()
|
||||||
|
if not self.check_token(BlockEntryToken,
|
||||||
|
KeyToken, ValueToken, BlockEndToken):
|
||||||
|
self.states.append(self.parse_indentless_sequence_entry)
|
||||||
|
return self.parse_block_node()
|
||||||
|
else:
|
||||||
|
self.state = self.parse_indentless_sequence_entry
|
||||||
|
return self.process_empty_scalar(token.end_mark)
|
||||||
|
token = self.peek_token()
|
||||||
|
event = SequenceEndEvent(token.start_mark, token.start_mark)
|
||||||
|
self.state = self.states.pop()
|
||||||
|
return event
|
||||||
|
|
||||||
|
# block_mapping ::= BLOCK-MAPPING_START
|
||||||
|
# ((KEY block_node_or_indentless_sequence?)?
|
||||||
|
# (VALUE block_node_or_indentless_sequence?)?)*
|
||||||
|
# BLOCK-END
|
||||||
|
|
||||||
|
def parse_block_mapping_first_key(self):
|
||||||
|
token = self.get_token()
|
||||||
|
self.marks.append(token.start_mark)
|
||||||
|
return self.parse_block_mapping_key()
|
||||||
|
|
||||||
|
def parse_block_mapping_key(self):
|
||||||
|
if self.check_token(KeyToken):
|
||||||
|
token = self.get_token()
|
||||||
|
if not self.check_token(KeyToken, ValueToken, BlockEndToken):
|
||||||
|
self.states.append(self.parse_block_mapping_value)
|
||||||
|
return self.parse_block_node_or_indentless_sequence()
|
||||||
|
else:
|
||||||
|
self.state = self.parse_block_mapping_value
|
||||||
|
return self.process_empty_scalar(token.end_mark)
|
||||||
|
if not self.check_token(BlockEndToken):
|
||||||
|
token = self.peek_token()
|
||||||
|
raise ParserError("while parsing a block mapping", self.marks[-1],
|
||||||
|
"expected <block end>, but found %r" % token.id, token.start_mark)
|
||||||
|
token = self.get_token()
|
||||||
|
event = MappingEndEvent(token.start_mark, token.end_mark)
|
||||||
|
self.state = self.states.pop()
|
||||||
|
self.marks.pop()
|
||||||
|
return event
|
||||||
|
|
||||||
|
def parse_block_mapping_value(self):
|
||||||
|
if self.check_token(ValueToken):
|
||||||
|
token = self.get_token()
|
||||||
|
if not self.check_token(KeyToken, ValueToken, BlockEndToken):
|
||||||
|
self.states.append(self.parse_block_mapping_key)
|
||||||
|
return self.parse_block_node_or_indentless_sequence()
|
||||||
|
else:
|
||||||
|
self.state = self.parse_block_mapping_key
|
||||||
|
return self.process_empty_scalar(token.end_mark)
|
||||||
|
else:
|
||||||
|
self.state = self.parse_block_mapping_key
|
||||||
|
token = self.peek_token()
|
||||||
|
return self.process_empty_scalar(token.start_mark)
|
||||||
|
|
||||||
|
# flow_sequence ::= FLOW-SEQUENCE-START
|
||||||
|
# (flow_sequence_entry FLOW-ENTRY)*
|
||||||
|
# flow_sequence_entry?
|
||||||
|
# FLOW-SEQUENCE-END
|
||||||
|
# flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)?
|
||||||
|
#
|
||||||
|
# Note that while production rules for both flow_sequence_entry and
|
||||||
|
# flow_mapping_entry are equal, their interpretations are different.
|
||||||
|
# For `flow_sequence_entry`, the part `KEY flow_node? (VALUE flow_node?)?`
|
||||||
|
# generate an inline mapping (set syntax).
|
||||||
|
|
||||||
|
def parse_flow_sequence_first_entry(self):
|
||||||
|
token = self.get_token()
|
||||||
|
self.marks.append(token.start_mark)
|
||||||
|
return self.parse_flow_sequence_entry(first=True)
|
||||||
|
|
||||||
|
def parse_flow_sequence_entry(self, first=False):
|
||||||
|
if not self.check_token(FlowSequenceEndToken):
|
||||||
|
if not first:
|
||||||
|
if self.check_token(FlowEntryToken):
|
||||||
|
self.get_token()
|
||||||
|
else:
|
||||||
|
token = self.peek_token()
|
||||||
|
raise ParserError("while parsing a flow sequence", self.marks[-1],
|
||||||
|
"expected ',' or ']', but got %r" % token.id, token.start_mark)
|
||||||
|
|
||||||
|
if self.check_token(KeyToken):
|
||||||
|
token = self.peek_token()
|
||||||
|
event = MappingStartEvent(None, None, True,
|
||||||
|
token.start_mark, token.end_mark,
|
||||||
|
flow_style=True)
|
||||||
|
self.state = self.parse_flow_sequence_entry_mapping_key
|
||||||
|
return event
|
||||||
|
elif not self.check_token(FlowSequenceEndToken):
|
||||||
|
self.states.append(self.parse_flow_sequence_entry)
|
||||||
|
return self.parse_flow_node()
|
||||||
|
token = self.get_token()
|
||||||
|
event = SequenceEndEvent(token.start_mark, token.end_mark)
|
||||||
|
self.state = self.states.pop()
|
||||||
|
self.marks.pop()
|
||||||
|
return event
|
||||||
|
|
||||||
|
def parse_flow_sequence_entry_mapping_key(self):
|
||||||
|
token = self.get_token()
|
||||||
|
if not self.check_token(ValueToken,
|
||||||
|
FlowEntryToken, FlowSequenceEndToken):
|
||||||
|
self.states.append(self.parse_flow_sequence_entry_mapping_value)
|
||||||
|
return self.parse_flow_node()
|
||||||
|
else:
|
||||||
|
self.state = self.parse_flow_sequence_entry_mapping_value
|
||||||
|
return self.process_empty_scalar(token.end_mark)
|
||||||
|
|
||||||
|
def parse_flow_sequence_entry_mapping_value(self):
|
||||||
|
if self.check_token(ValueToken):
|
||||||
|
token = self.get_token()
|
||||||
|
if not self.check_token(FlowEntryToken, FlowSequenceEndToken):
|
||||||
|
self.states.append(self.parse_flow_sequence_entry_mapping_end)
|
||||||
|
return self.parse_flow_node()
|
||||||
|
else:
|
||||||
|
self.state = self.parse_flow_sequence_entry_mapping_end
|
||||||
|
return self.process_empty_scalar(token.end_mark)
|
||||||
|
else:
|
||||||
|
self.state = self.parse_flow_sequence_entry_mapping_end
|
||||||
|
token = self.peek_token()
|
||||||
|
return self.process_empty_scalar(token.start_mark)
|
||||||
|
|
||||||
|
def parse_flow_sequence_entry_mapping_end(self):
|
||||||
|
self.state = self.parse_flow_sequence_entry
|
||||||
|
token = self.peek_token()
|
||||||
|
return MappingEndEvent(token.start_mark, token.start_mark)
|
||||||
|
|
||||||
|
# flow_mapping ::= FLOW-MAPPING-START
|
||||||
|
# (flow_mapping_entry FLOW-ENTRY)*
|
||||||
|
# flow_mapping_entry?
|
||||||
|
# FLOW-MAPPING-END
|
||||||
|
# flow_mapping_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)?
|
||||||
|
|
||||||
|
def parse_flow_mapping_first_key(self):
|
||||||
|
token = self.get_token()
|
||||||
|
self.marks.append(token.start_mark)
|
||||||
|
return self.parse_flow_mapping_key(first=True)
|
||||||
|
|
||||||
|
def parse_flow_mapping_key(self, first=False):
|
||||||
|
if not self.check_token(FlowMappingEndToken):
|
||||||
|
if not first:
|
||||||
|
if self.check_token(FlowEntryToken):
|
||||||
|
self.get_token()
|
||||||
|
else:
|
||||||
|
token = self.peek_token()
|
||||||
|
raise ParserError("while parsing a flow mapping", self.marks[-1],
|
||||||
|
"expected ',' or '}', but got %r" % token.id, token.start_mark)
|
||||||
|
if self.check_token(KeyToken):
|
||||||
|
token = self.get_token()
|
||||||
|
if not self.check_token(ValueToken,
|
||||||
|
FlowEntryToken, FlowMappingEndToken):
|
||||||
|
self.states.append(self.parse_flow_mapping_value)
|
||||||
|
return self.parse_flow_node()
|
||||||
|
else:
|
||||||
|
self.state = self.parse_flow_mapping_value
|
||||||
|
return self.process_empty_scalar(token.end_mark)
|
||||||
|
elif not self.check_token(FlowMappingEndToken):
|
||||||
|
self.states.append(self.parse_flow_mapping_empty_value)
|
||||||
|
return self.parse_flow_node()
|
||||||
|
token = self.get_token()
|
||||||
|
event = MappingEndEvent(token.start_mark, token.end_mark)
|
||||||
|
self.state = self.states.pop()
|
||||||
|
self.marks.pop()
|
||||||
|
return event
|
||||||
|
|
||||||
|
def parse_flow_mapping_value(self):
|
||||||
|
if self.check_token(ValueToken):
|
||||||
|
token = self.get_token()
|
||||||
|
if not self.check_token(FlowEntryToken, FlowMappingEndToken):
|
||||||
|
self.states.append(self.parse_flow_mapping_key)
|
||||||
|
return self.parse_flow_node()
|
||||||
|
else:
|
||||||
|
self.state = self.parse_flow_mapping_key
|
||||||
|
return self.process_empty_scalar(token.end_mark)
|
||||||
|
else:
|
||||||
|
self.state = self.parse_flow_mapping_key
|
||||||
|
token = self.peek_token()
|
||||||
|
return self.process_empty_scalar(token.start_mark)
|
||||||
|
|
||||||
|
def parse_flow_mapping_empty_value(self):
|
||||||
|
self.state = self.parse_flow_mapping_key
|
||||||
|
return self.process_empty_scalar(self.peek_token().start_mark)
|
||||||
|
|
||||||
|
def process_empty_scalar(self, mark):
|
||||||
|
return ScalarEvent(None, None, (True, False), '', mark, mark)
|
||||||
|
|
||||||
185
lib/sunhpc/yaml/reader.py
Normal file
185
lib/sunhpc/yaml/reader.py
Normal file
@@ -0,0 +1,185 @@
|
|||||||
|
# This module contains abstractions for the input stream. You don't have to
|
||||||
|
# looks further, there are no pretty code.
|
||||||
|
#
|
||||||
|
# We define two classes here.
|
||||||
|
#
|
||||||
|
# Mark(source, line, column)
|
||||||
|
# It's just a record and its only use is producing nice error messages.
|
||||||
|
# Parser does not use it for any other purposes.
|
||||||
|
#
|
||||||
|
# Reader(source, data)
|
||||||
|
# Reader determines the encoding of `data` and converts it to unicode.
|
||||||
|
# Reader provides the following methods and attributes:
|
||||||
|
# reader.peek(length=1) - return the next `length` characters
|
||||||
|
# reader.forward(length=1) - move the current position to `length` characters.
|
||||||
|
# reader.index - the number of the current character.
|
||||||
|
# reader.line, stream.column - the line and the column of the current character.
|
||||||
|
|
||||||
|
__all__ = ['Reader', 'ReaderError']
|
||||||
|
|
||||||
|
from .error import YAMLError, Mark
|
||||||
|
|
||||||
|
import codecs, re
|
||||||
|
|
||||||
|
class ReaderError(YAMLError):
|
||||||
|
|
||||||
|
def __init__(self, name, position, character, encoding, reason):
|
||||||
|
self.name = name
|
||||||
|
self.character = character
|
||||||
|
self.position = position
|
||||||
|
self.encoding = encoding
|
||||||
|
self.reason = reason
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
if isinstance(self.character, bytes):
|
||||||
|
return "'%s' codec can't decode byte #x%02x: %s\n" \
|
||||||
|
" in \"%s\", position %d" \
|
||||||
|
% (self.encoding, ord(self.character), self.reason,
|
||||||
|
self.name, self.position)
|
||||||
|
else:
|
||||||
|
return "unacceptable character #x%04x: %s\n" \
|
||||||
|
" in \"%s\", position %d" \
|
||||||
|
% (self.character, self.reason,
|
||||||
|
self.name, self.position)
|
||||||
|
|
||||||
|
class Reader(object):
|
||||||
|
# Reader:
|
||||||
|
# - determines the data encoding and converts it to a unicode string,
|
||||||
|
# - checks if characters are in allowed range,
|
||||||
|
# - adds '\0' to the end.
|
||||||
|
|
||||||
|
# Reader accepts
|
||||||
|
# - a `bytes` object,
|
||||||
|
# - a `str` object,
|
||||||
|
# - a file-like object with its `read` method returning `str`,
|
||||||
|
# - a file-like object with its `read` method returning `unicode`.
|
||||||
|
|
||||||
|
# Yeah, it's ugly and slow.
|
||||||
|
|
||||||
|
def __init__(self, stream):
|
||||||
|
self.name = None
|
||||||
|
self.stream = None
|
||||||
|
self.stream_pointer = 0
|
||||||
|
self.eof = True
|
||||||
|
self.buffer = ''
|
||||||
|
self.pointer = 0
|
||||||
|
self.raw_buffer = None
|
||||||
|
self.raw_decode = None
|
||||||
|
self.encoding = None
|
||||||
|
self.index = 0
|
||||||
|
self.line = 0
|
||||||
|
self.column = 0
|
||||||
|
if isinstance(stream, str):
|
||||||
|
self.name = "<unicode string>"
|
||||||
|
self.check_printable(stream)
|
||||||
|
self.buffer = stream+'\0'
|
||||||
|
elif isinstance(stream, bytes):
|
||||||
|
self.name = "<byte string>"
|
||||||
|
self.raw_buffer = stream
|
||||||
|
self.determine_encoding()
|
||||||
|
else:
|
||||||
|
self.stream = stream
|
||||||
|
self.name = getattr(stream, 'name', "<file>")
|
||||||
|
self.eof = False
|
||||||
|
self.raw_buffer = None
|
||||||
|
self.determine_encoding()
|
||||||
|
|
||||||
|
def peek(self, index=0):
|
||||||
|
try:
|
||||||
|
return self.buffer[self.pointer+index]
|
||||||
|
except IndexError:
|
||||||
|
self.update(index+1)
|
||||||
|
return self.buffer[self.pointer+index]
|
||||||
|
|
||||||
|
def prefix(self, length=1):
|
||||||
|
if self.pointer+length >= len(self.buffer):
|
||||||
|
self.update(length)
|
||||||
|
return self.buffer[self.pointer:self.pointer+length]
|
||||||
|
|
||||||
|
def forward(self, length=1):
|
||||||
|
if self.pointer+length+1 >= len(self.buffer):
|
||||||
|
self.update(length+1)
|
||||||
|
while length:
|
||||||
|
ch = self.buffer[self.pointer]
|
||||||
|
self.pointer += 1
|
||||||
|
self.index += 1
|
||||||
|
if ch in '\n\x85\u2028\u2029' \
|
||||||
|
or (ch == '\r' and self.buffer[self.pointer] != '\n'):
|
||||||
|
self.line += 1
|
||||||
|
self.column = 0
|
||||||
|
elif ch != '\uFEFF':
|
||||||
|
self.column += 1
|
||||||
|
length -= 1
|
||||||
|
|
||||||
|
def get_mark(self):
|
||||||
|
if self.stream is None:
|
||||||
|
return Mark(self.name, self.index, self.line, self.column,
|
||||||
|
self.buffer, self.pointer)
|
||||||
|
else:
|
||||||
|
return Mark(self.name, self.index, self.line, self.column,
|
||||||
|
None, None)
|
||||||
|
|
||||||
|
def determine_encoding(self):
|
||||||
|
while not self.eof and (self.raw_buffer is None or len(self.raw_buffer) < 2):
|
||||||
|
self.update_raw()
|
||||||
|
if isinstance(self.raw_buffer, bytes):
|
||||||
|
if self.raw_buffer.startswith(codecs.BOM_UTF16_LE):
|
||||||
|
self.raw_decode = codecs.utf_16_le_decode
|
||||||
|
self.encoding = 'utf-16-le'
|
||||||
|
elif self.raw_buffer.startswith(codecs.BOM_UTF16_BE):
|
||||||
|
self.raw_decode = codecs.utf_16_be_decode
|
||||||
|
self.encoding = 'utf-16-be'
|
||||||
|
else:
|
||||||
|
self.raw_decode = codecs.utf_8_decode
|
||||||
|
self.encoding = 'utf-8'
|
||||||
|
self.update(1)
|
||||||
|
|
||||||
|
NON_PRINTABLE = re.compile('[^\x09\x0A\x0D\x20-\x7E\x85\xA0-\uD7FF\uE000-\uFFFD\U00010000-\U0010ffff]')
|
||||||
|
def check_printable(self, data):
|
||||||
|
match = self.NON_PRINTABLE.search(data)
|
||||||
|
if match:
|
||||||
|
character = match.group()
|
||||||
|
position = self.index+(len(self.buffer)-self.pointer)+match.start()
|
||||||
|
raise ReaderError(self.name, position, ord(character),
|
||||||
|
'unicode', "special characters are not allowed")
|
||||||
|
|
||||||
|
def update(self, length):
|
||||||
|
if self.raw_buffer is None:
|
||||||
|
return
|
||||||
|
self.buffer = self.buffer[self.pointer:]
|
||||||
|
self.pointer = 0
|
||||||
|
while len(self.buffer) < length:
|
||||||
|
if not self.eof:
|
||||||
|
self.update_raw()
|
||||||
|
if self.raw_decode is not None:
|
||||||
|
try:
|
||||||
|
data, converted = self.raw_decode(self.raw_buffer,
|
||||||
|
'strict', self.eof)
|
||||||
|
except UnicodeDecodeError as exc:
|
||||||
|
character = self.raw_buffer[exc.start]
|
||||||
|
if self.stream is not None:
|
||||||
|
position = self.stream_pointer-len(self.raw_buffer)+exc.start
|
||||||
|
else:
|
||||||
|
position = exc.start
|
||||||
|
raise ReaderError(self.name, position, character,
|
||||||
|
exc.encoding, exc.reason)
|
||||||
|
else:
|
||||||
|
data = self.raw_buffer
|
||||||
|
converted = len(data)
|
||||||
|
self.check_printable(data)
|
||||||
|
self.buffer += data
|
||||||
|
self.raw_buffer = self.raw_buffer[converted:]
|
||||||
|
if self.eof:
|
||||||
|
self.buffer += '\0'
|
||||||
|
self.raw_buffer = None
|
||||||
|
break
|
||||||
|
|
||||||
|
def update_raw(self, size=4096):
|
||||||
|
data = self.stream.read(size)
|
||||||
|
if self.raw_buffer is None:
|
||||||
|
self.raw_buffer = data
|
||||||
|
else:
|
||||||
|
self.raw_buffer += data
|
||||||
|
self.stream_pointer += len(data)
|
||||||
|
if not data:
|
||||||
|
self.eof = True
|
||||||
389
lib/sunhpc/yaml/representer.py
Normal file
389
lib/sunhpc/yaml/representer.py
Normal file
@@ -0,0 +1,389 @@
|
|||||||
|
|
||||||
|
__all__ = ['BaseRepresenter', 'SafeRepresenter', 'Representer',
|
||||||
|
'RepresenterError']
|
||||||
|
|
||||||
|
from .error import *
|
||||||
|
from .nodes import *
|
||||||
|
|
||||||
|
import datetime, copyreg, types, base64, collections
|
||||||
|
|
||||||
|
class RepresenterError(YAMLError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class BaseRepresenter:
|
||||||
|
|
||||||
|
yaml_representers = {}
|
||||||
|
yaml_multi_representers = {}
|
||||||
|
|
||||||
|
def __init__(self, default_style=None, default_flow_style=False, sort_keys=True):
|
||||||
|
self.default_style = default_style
|
||||||
|
self.sort_keys = sort_keys
|
||||||
|
self.default_flow_style = default_flow_style
|
||||||
|
self.represented_objects = {}
|
||||||
|
self.object_keeper = []
|
||||||
|
self.alias_key = None
|
||||||
|
|
||||||
|
def represent(self, data):
|
||||||
|
node = self.represent_data(data)
|
||||||
|
self.serialize(node)
|
||||||
|
self.represented_objects = {}
|
||||||
|
self.object_keeper = []
|
||||||
|
self.alias_key = None
|
||||||
|
|
||||||
|
def represent_data(self, data):
|
||||||
|
if self.ignore_aliases(data):
|
||||||
|
self.alias_key = None
|
||||||
|
else:
|
||||||
|
self.alias_key = id(data)
|
||||||
|
if self.alias_key is not None:
|
||||||
|
if self.alias_key in self.represented_objects:
|
||||||
|
node = self.represented_objects[self.alias_key]
|
||||||
|
#if node is None:
|
||||||
|
# raise RepresenterError("recursive objects are not allowed: %r" % data)
|
||||||
|
return node
|
||||||
|
#self.represented_objects[alias_key] = None
|
||||||
|
self.object_keeper.append(data)
|
||||||
|
data_types = type(data).__mro__
|
||||||
|
if data_types[0] in self.yaml_representers:
|
||||||
|
node = self.yaml_representers[data_types[0]](self, data)
|
||||||
|
else:
|
||||||
|
for data_type in data_types:
|
||||||
|
if data_type in self.yaml_multi_representers:
|
||||||
|
node = self.yaml_multi_representers[data_type](self, data)
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
if None in self.yaml_multi_representers:
|
||||||
|
node = self.yaml_multi_representers[None](self, data)
|
||||||
|
elif None in self.yaml_representers:
|
||||||
|
node = self.yaml_representers[None](self, data)
|
||||||
|
else:
|
||||||
|
node = ScalarNode(None, str(data))
|
||||||
|
#if alias_key is not None:
|
||||||
|
# self.represented_objects[alias_key] = node
|
||||||
|
return node
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def add_representer(cls, data_type, representer):
|
||||||
|
if not 'yaml_representers' in cls.__dict__:
|
||||||
|
cls.yaml_representers = cls.yaml_representers.copy()
|
||||||
|
cls.yaml_representers[data_type] = representer
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def add_multi_representer(cls, data_type, representer):
|
||||||
|
if not 'yaml_multi_representers' in cls.__dict__:
|
||||||
|
cls.yaml_multi_representers = cls.yaml_multi_representers.copy()
|
||||||
|
cls.yaml_multi_representers[data_type] = representer
|
||||||
|
|
||||||
|
def represent_scalar(self, tag, value, style=None):
|
||||||
|
if style is None:
|
||||||
|
style = self.default_style
|
||||||
|
node = ScalarNode(tag, value, style=style)
|
||||||
|
if self.alias_key is not None:
|
||||||
|
self.represented_objects[self.alias_key] = node
|
||||||
|
return node
|
||||||
|
|
||||||
|
def represent_sequence(self, tag, sequence, flow_style=None):
|
||||||
|
value = []
|
||||||
|
node = SequenceNode(tag, value, flow_style=flow_style)
|
||||||
|
if self.alias_key is not None:
|
||||||
|
self.represented_objects[self.alias_key] = node
|
||||||
|
best_style = True
|
||||||
|
for item in sequence:
|
||||||
|
node_item = self.represent_data(item)
|
||||||
|
if not (isinstance(node_item, ScalarNode) and not node_item.style):
|
||||||
|
best_style = False
|
||||||
|
value.append(node_item)
|
||||||
|
if flow_style is None:
|
||||||
|
if self.default_flow_style is not None:
|
||||||
|
node.flow_style = self.default_flow_style
|
||||||
|
else:
|
||||||
|
node.flow_style = best_style
|
||||||
|
return node
|
||||||
|
|
||||||
|
def represent_mapping(self, tag, mapping, flow_style=None):
|
||||||
|
value = []
|
||||||
|
node = MappingNode(tag, value, flow_style=flow_style)
|
||||||
|
if self.alias_key is not None:
|
||||||
|
self.represented_objects[self.alias_key] = node
|
||||||
|
best_style = True
|
||||||
|
if hasattr(mapping, 'items'):
|
||||||
|
mapping = list(mapping.items())
|
||||||
|
if self.sort_keys:
|
||||||
|
try:
|
||||||
|
mapping = sorted(mapping)
|
||||||
|
except TypeError:
|
||||||
|
pass
|
||||||
|
for item_key, item_value in mapping:
|
||||||
|
node_key = self.represent_data(item_key)
|
||||||
|
node_value = self.represent_data(item_value)
|
||||||
|
if not (isinstance(node_key, ScalarNode) and not node_key.style):
|
||||||
|
best_style = False
|
||||||
|
if not (isinstance(node_value, ScalarNode) and not node_value.style):
|
||||||
|
best_style = False
|
||||||
|
value.append((node_key, node_value))
|
||||||
|
if flow_style is None:
|
||||||
|
if self.default_flow_style is not None:
|
||||||
|
node.flow_style = self.default_flow_style
|
||||||
|
else:
|
||||||
|
node.flow_style = best_style
|
||||||
|
return node
|
||||||
|
|
||||||
|
def ignore_aliases(self, data):
|
||||||
|
return False
|
||||||
|
|
||||||
|
class SafeRepresenter(BaseRepresenter):
|
||||||
|
|
||||||
|
def ignore_aliases(self, data):
|
||||||
|
if data is None:
|
||||||
|
return True
|
||||||
|
if isinstance(data, tuple) and data == ():
|
||||||
|
return True
|
||||||
|
if isinstance(data, (str, bytes, bool, int, float)):
|
||||||
|
return True
|
||||||
|
|
||||||
|
def represent_none(self, data):
|
||||||
|
return self.represent_scalar('tag:yaml.org,2002:null', 'null')
|
||||||
|
|
||||||
|
def represent_str(self, data):
|
||||||
|
return self.represent_scalar('tag:yaml.org,2002:str', data)
|
||||||
|
|
||||||
|
def represent_binary(self, data):
|
||||||
|
if hasattr(base64, 'encodebytes'):
|
||||||
|
data = base64.encodebytes(data).decode('ascii')
|
||||||
|
else:
|
||||||
|
data = base64.encodestring(data).decode('ascii')
|
||||||
|
return self.represent_scalar('tag:yaml.org,2002:binary', data, style='|')
|
||||||
|
|
||||||
|
def represent_bool(self, data):
|
||||||
|
if data:
|
||||||
|
value = 'true'
|
||||||
|
else:
|
||||||
|
value = 'false'
|
||||||
|
return self.represent_scalar('tag:yaml.org,2002:bool', value)
|
||||||
|
|
||||||
|
def represent_int(self, data):
|
||||||
|
return self.represent_scalar('tag:yaml.org,2002:int', str(data))
|
||||||
|
|
||||||
|
inf_value = 1e300
|
||||||
|
while repr(inf_value) != repr(inf_value*inf_value):
|
||||||
|
inf_value *= inf_value
|
||||||
|
|
||||||
|
def represent_float(self, data):
|
||||||
|
if data != data or (data == 0.0 and data == 1.0):
|
||||||
|
value = '.nan'
|
||||||
|
elif data == self.inf_value:
|
||||||
|
value = '.inf'
|
||||||
|
elif data == -self.inf_value:
|
||||||
|
value = '-.inf'
|
||||||
|
else:
|
||||||
|
value = repr(data).lower()
|
||||||
|
# Note that in some cases `repr(data)` represents a float number
|
||||||
|
# without the decimal parts. For instance:
|
||||||
|
# >>> repr(1e17)
|
||||||
|
# '1e17'
|
||||||
|
# Unfortunately, this is not a valid float representation according
|
||||||
|
# to the definition of the `!!float` tag. We fix this by adding
|
||||||
|
# '.0' before the 'e' symbol.
|
||||||
|
if '.' not in value and 'e' in value:
|
||||||
|
value = value.replace('e', '.0e', 1)
|
||||||
|
return self.represent_scalar('tag:yaml.org,2002:float', value)
|
||||||
|
|
||||||
|
def represent_list(self, data):
|
||||||
|
#pairs = (len(data) > 0 and isinstance(data, list))
|
||||||
|
#if pairs:
|
||||||
|
# for item in data:
|
||||||
|
# if not isinstance(item, tuple) or len(item) != 2:
|
||||||
|
# pairs = False
|
||||||
|
# break
|
||||||
|
#if not pairs:
|
||||||
|
return self.represent_sequence('tag:yaml.org,2002:seq', data)
|
||||||
|
#value = []
|
||||||
|
#for item_key, item_value in data:
|
||||||
|
# value.append(self.represent_mapping(u'tag:yaml.org,2002:map',
|
||||||
|
# [(item_key, item_value)]))
|
||||||
|
#return SequenceNode(u'tag:yaml.org,2002:pairs', value)
|
||||||
|
|
||||||
|
def represent_dict(self, data):
|
||||||
|
return self.represent_mapping('tag:yaml.org,2002:map', data)
|
||||||
|
|
||||||
|
def represent_set(self, data):
|
||||||
|
value = {}
|
||||||
|
for key in data:
|
||||||
|
value[key] = None
|
||||||
|
return self.represent_mapping('tag:yaml.org,2002:set', value)
|
||||||
|
|
||||||
|
def represent_date(self, data):
|
||||||
|
value = data.isoformat()
|
||||||
|
return self.represent_scalar('tag:yaml.org,2002:timestamp', value)
|
||||||
|
|
||||||
|
def represent_datetime(self, data):
|
||||||
|
value = data.isoformat(' ')
|
||||||
|
return self.represent_scalar('tag:yaml.org,2002:timestamp', value)
|
||||||
|
|
||||||
|
def represent_yaml_object(self, tag, data, cls, flow_style=None):
|
||||||
|
if hasattr(data, '__getstate__'):
|
||||||
|
state = data.__getstate__()
|
||||||
|
else:
|
||||||
|
state = data.__dict__.copy()
|
||||||
|
return self.represent_mapping(tag, state, flow_style=flow_style)
|
||||||
|
|
||||||
|
def represent_undefined(self, data):
|
||||||
|
raise RepresenterError("cannot represent an object", data)
|
||||||
|
|
||||||
|
SafeRepresenter.add_representer(type(None),
|
||||||
|
SafeRepresenter.represent_none)
|
||||||
|
|
||||||
|
SafeRepresenter.add_representer(str,
|
||||||
|
SafeRepresenter.represent_str)
|
||||||
|
|
||||||
|
SafeRepresenter.add_representer(bytes,
|
||||||
|
SafeRepresenter.represent_binary)
|
||||||
|
|
||||||
|
SafeRepresenter.add_representer(bool,
|
||||||
|
SafeRepresenter.represent_bool)
|
||||||
|
|
||||||
|
SafeRepresenter.add_representer(int,
|
||||||
|
SafeRepresenter.represent_int)
|
||||||
|
|
||||||
|
SafeRepresenter.add_representer(float,
|
||||||
|
SafeRepresenter.represent_float)
|
||||||
|
|
||||||
|
SafeRepresenter.add_representer(list,
|
||||||
|
SafeRepresenter.represent_list)
|
||||||
|
|
||||||
|
SafeRepresenter.add_representer(tuple,
|
||||||
|
SafeRepresenter.represent_list)
|
||||||
|
|
||||||
|
SafeRepresenter.add_representer(dict,
|
||||||
|
SafeRepresenter.represent_dict)
|
||||||
|
|
||||||
|
SafeRepresenter.add_representer(set,
|
||||||
|
SafeRepresenter.represent_set)
|
||||||
|
|
||||||
|
SafeRepresenter.add_representer(datetime.date,
|
||||||
|
SafeRepresenter.represent_date)
|
||||||
|
|
||||||
|
SafeRepresenter.add_representer(datetime.datetime,
|
||||||
|
SafeRepresenter.represent_datetime)
|
||||||
|
|
||||||
|
SafeRepresenter.add_representer(None,
|
||||||
|
SafeRepresenter.represent_undefined)
|
||||||
|
|
||||||
|
class Representer(SafeRepresenter):
|
||||||
|
|
||||||
|
def represent_complex(self, data):
|
||||||
|
if data.imag == 0.0:
|
||||||
|
data = '%r' % data.real
|
||||||
|
elif data.real == 0.0:
|
||||||
|
data = '%rj' % data.imag
|
||||||
|
elif data.imag > 0:
|
||||||
|
data = '%r+%rj' % (data.real, data.imag)
|
||||||
|
else:
|
||||||
|
data = '%r%rj' % (data.real, data.imag)
|
||||||
|
return self.represent_scalar('tag:yaml.org,2002:python/complex', data)
|
||||||
|
|
||||||
|
def represent_tuple(self, data):
|
||||||
|
return self.represent_sequence('tag:yaml.org,2002:python/tuple', data)
|
||||||
|
|
||||||
|
def represent_name(self, data):
|
||||||
|
name = '%s.%s' % (data.__module__, data.__name__)
|
||||||
|
return self.represent_scalar('tag:yaml.org,2002:python/name:'+name, '')
|
||||||
|
|
||||||
|
def represent_module(self, data):
|
||||||
|
return self.represent_scalar(
|
||||||
|
'tag:yaml.org,2002:python/module:'+data.__name__, '')
|
||||||
|
|
||||||
|
def represent_object(self, data):
|
||||||
|
# We use __reduce__ API to save the data. data.__reduce__ returns
|
||||||
|
# a tuple of length 2-5:
|
||||||
|
# (function, args, state, listitems, dictitems)
|
||||||
|
|
||||||
|
# For reconstructing, we calls function(*args), then set its state,
|
||||||
|
# listitems, and dictitems if they are not None.
|
||||||
|
|
||||||
|
# A special case is when function.__name__ == '__newobj__'. In this
|
||||||
|
# case we create the object with args[0].__new__(*args).
|
||||||
|
|
||||||
|
# Another special case is when __reduce__ returns a string - we don't
|
||||||
|
# support it.
|
||||||
|
|
||||||
|
# We produce a !!python/object, !!python/object/new or
|
||||||
|
# !!python/object/apply node.
|
||||||
|
|
||||||
|
cls = type(data)
|
||||||
|
if cls in copyreg.dispatch_table:
|
||||||
|
reduce = copyreg.dispatch_table[cls](data)
|
||||||
|
elif hasattr(data, '__reduce_ex__'):
|
||||||
|
reduce = data.__reduce_ex__(2)
|
||||||
|
elif hasattr(data, '__reduce__'):
|
||||||
|
reduce = data.__reduce__()
|
||||||
|
else:
|
||||||
|
raise RepresenterError("cannot represent an object", data)
|
||||||
|
reduce = (list(reduce)+[None]*5)[:5]
|
||||||
|
function, args, state, listitems, dictitems = reduce
|
||||||
|
args = list(args)
|
||||||
|
if state is None:
|
||||||
|
state = {}
|
||||||
|
if listitems is not None:
|
||||||
|
listitems = list(listitems)
|
||||||
|
if dictitems is not None:
|
||||||
|
dictitems = dict(dictitems)
|
||||||
|
if function.__name__ == '__newobj__':
|
||||||
|
function = args[0]
|
||||||
|
args = args[1:]
|
||||||
|
tag = 'tag:yaml.org,2002:python/object/new:'
|
||||||
|
newobj = True
|
||||||
|
else:
|
||||||
|
tag = 'tag:yaml.org,2002:python/object/apply:'
|
||||||
|
newobj = False
|
||||||
|
function_name = '%s.%s' % (function.__module__, function.__name__)
|
||||||
|
if not args and not listitems and not dictitems \
|
||||||
|
and isinstance(state, dict) and newobj:
|
||||||
|
return self.represent_mapping(
|
||||||
|
'tag:yaml.org,2002:python/object:'+function_name, state)
|
||||||
|
if not listitems and not dictitems \
|
||||||
|
and isinstance(state, dict) and not state:
|
||||||
|
return self.represent_sequence(tag+function_name, args)
|
||||||
|
value = {}
|
||||||
|
if args:
|
||||||
|
value['args'] = args
|
||||||
|
if state or not isinstance(state, dict):
|
||||||
|
value['state'] = state
|
||||||
|
if listitems:
|
||||||
|
value['listitems'] = listitems
|
||||||
|
if dictitems:
|
||||||
|
value['dictitems'] = dictitems
|
||||||
|
return self.represent_mapping(tag+function_name, value)
|
||||||
|
|
||||||
|
def represent_ordered_dict(self, data):
|
||||||
|
# Provide uniform representation across different Python versions.
|
||||||
|
data_type = type(data)
|
||||||
|
tag = 'tag:yaml.org,2002:python/object/apply:%s.%s' \
|
||||||
|
% (data_type.__module__, data_type.__name__)
|
||||||
|
items = [[key, value] for key, value in data.items()]
|
||||||
|
return self.represent_sequence(tag, [items])
|
||||||
|
|
||||||
|
Representer.add_representer(complex,
|
||||||
|
Representer.represent_complex)
|
||||||
|
|
||||||
|
Representer.add_representer(tuple,
|
||||||
|
Representer.represent_tuple)
|
||||||
|
|
||||||
|
Representer.add_multi_representer(type,
|
||||||
|
Representer.represent_name)
|
||||||
|
|
||||||
|
Representer.add_representer(collections.OrderedDict,
|
||||||
|
Representer.represent_ordered_dict)
|
||||||
|
|
||||||
|
Representer.add_representer(types.FunctionType,
|
||||||
|
Representer.represent_name)
|
||||||
|
|
||||||
|
Representer.add_representer(types.BuiltinFunctionType,
|
||||||
|
Representer.represent_name)
|
||||||
|
|
||||||
|
Representer.add_representer(types.ModuleType,
|
||||||
|
Representer.represent_module)
|
||||||
|
|
||||||
|
Representer.add_multi_representer(object,
|
||||||
|
Representer.represent_object)
|
||||||
|
|
||||||
227
lib/sunhpc/yaml/resolver.py
Normal file
227
lib/sunhpc/yaml/resolver.py
Normal file
@@ -0,0 +1,227 @@
|
|||||||
|
|
||||||
|
__all__ = ['BaseResolver', 'Resolver']
|
||||||
|
|
||||||
|
from .error import *
|
||||||
|
from .nodes import *
|
||||||
|
|
||||||
|
import re
|
||||||
|
|
||||||
|
class ResolverError(YAMLError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class BaseResolver:
|
||||||
|
|
||||||
|
DEFAULT_SCALAR_TAG = 'tag:yaml.org,2002:str'
|
||||||
|
DEFAULT_SEQUENCE_TAG = 'tag:yaml.org,2002:seq'
|
||||||
|
DEFAULT_MAPPING_TAG = 'tag:yaml.org,2002:map'
|
||||||
|
|
||||||
|
yaml_implicit_resolvers = {}
|
||||||
|
yaml_path_resolvers = {}
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.resolver_exact_paths = []
|
||||||
|
self.resolver_prefix_paths = []
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def add_implicit_resolver(cls, tag, regexp, first):
|
||||||
|
if not 'yaml_implicit_resolvers' in cls.__dict__:
|
||||||
|
implicit_resolvers = {}
|
||||||
|
for key in cls.yaml_implicit_resolvers:
|
||||||
|
implicit_resolvers[key] = cls.yaml_implicit_resolvers[key][:]
|
||||||
|
cls.yaml_implicit_resolvers = implicit_resolvers
|
||||||
|
if first is None:
|
||||||
|
first = [None]
|
||||||
|
for ch in first:
|
||||||
|
cls.yaml_implicit_resolvers.setdefault(ch, []).append((tag, regexp))
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def add_path_resolver(cls, tag, path, kind=None):
|
||||||
|
# Note: `add_path_resolver` is experimental. The API could be changed.
|
||||||
|
# `new_path` is a pattern that is matched against the path from the
|
||||||
|
# root to the node that is being considered. `node_path` elements are
|
||||||
|
# tuples `(node_check, index_check)`. `node_check` is a node class:
|
||||||
|
# `ScalarNode`, `SequenceNode`, `MappingNode` or `None`. `None`
|
||||||
|
# matches any kind of a node. `index_check` could be `None`, a boolean
|
||||||
|
# value, a string value, or a number. `None` and `False` match against
|
||||||
|
# any _value_ of sequence and mapping nodes. `True` matches against
|
||||||
|
# any _key_ of a mapping node. A string `index_check` matches against
|
||||||
|
# a mapping value that corresponds to a scalar key which content is
|
||||||
|
# equal to the `index_check` value. An integer `index_check` matches
|
||||||
|
# against a sequence value with the index equal to `index_check`.
|
||||||
|
if not 'yaml_path_resolvers' in cls.__dict__:
|
||||||
|
cls.yaml_path_resolvers = cls.yaml_path_resolvers.copy()
|
||||||
|
new_path = []
|
||||||
|
for element in path:
|
||||||
|
if isinstance(element, (list, tuple)):
|
||||||
|
if len(element) == 2:
|
||||||
|
node_check, index_check = element
|
||||||
|
elif len(element) == 1:
|
||||||
|
node_check = element[0]
|
||||||
|
index_check = True
|
||||||
|
else:
|
||||||
|
raise ResolverError("Invalid path element: %s" % element)
|
||||||
|
else:
|
||||||
|
node_check = None
|
||||||
|
index_check = element
|
||||||
|
if node_check is str:
|
||||||
|
node_check = ScalarNode
|
||||||
|
elif node_check is list:
|
||||||
|
node_check = SequenceNode
|
||||||
|
elif node_check is dict:
|
||||||
|
node_check = MappingNode
|
||||||
|
elif node_check not in [ScalarNode, SequenceNode, MappingNode] \
|
||||||
|
and not isinstance(node_check, str) \
|
||||||
|
and node_check is not None:
|
||||||
|
raise ResolverError("Invalid node checker: %s" % node_check)
|
||||||
|
if not isinstance(index_check, (str, int)) \
|
||||||
|
and index_check is not None:
|
||||||
|
raise ResolverError("Invalid index checker: %s" % index_check)
|
||||||
|
new_path.append((node_check, index_check))
|
||||||
|
if kind is str:
|
||||||
|
kind = ScalarNode
|
||||||
|
elif kind is list:
|
||||||
|
kind = SequenceNode
|
||||||
|
elif kind is dict:
|
||||||
|
kind = MappingNode
|
||||||
|
elif kind not in [ScalarNode, SequenceNode, MappingNode] \
|
||||||
|
and kind is not None:
|
||||||
|
raise ResolverError("Invalid node kind: %s" % kind)
|
||||||
|
cls.yaml_path_resolvers[tuple(new_path), kind] = tag
|
||||||
|
|
||||||
|
def descend_resolver(self, current_node, current_index):
|
||||||
|
if not self.yaml_path_resolvers:
|
||||||
|
return
|
||||||
|
exact_paths = {}
|
||||||
|
prefix_paths = []
|
||||||
|
if current_node:
|
||||||
|
depth = len(self.resolver_prefix_paths)
|
||||||
|
for path, kind in self.resolver_prefix_paths[-1]:
|
||||||
|
if self.check_resolver_prefix(depth, path, kind,
|
||||||
|
current_node, current_index):
|
||||||
|
if len(path) > depth:
|
||||||
|
prefix_paths.append((path, kind))
|
||||||
|
else:
|
||||||
|
exact_paths[kind] = self.yaml_path_resolvers[path, kind]
|
||||||
|
else:
|
||||||
|
for path, kind in self.yaml_path_resolvers:
|
||||||
|
if not path:
|
||||||
|
exact_paths[kind] = self.yaml_path_resolvers[path, kind]
|
||||||
|
else:
|
||||||
|
prefix_paths.append((path, kind))
|
||||||
|
self.resolver_exact_paths.append(exact_paths)
|
||||||
|
self.resolver_prefix_paths.append(prefix_paths)
|
||||||
|
|
||||||
|
def ascend_resolver(self):
|
||||||
|
if not self.yaml_path_resolvers:
|
||||||
|
return
|
||||||
|
self.resolver_exact_paths.pop()
|
||||||
|
self.resolver_prefix_paths.pop()
|
||||||
|
|
||||||
|
def check_resolver_prefix(self, depth, path, kind,
|
||||||
|
current_node, current_index):
|
||||||
|
node_check, index_check = path[depth-1]
|
||||||
|
if isinstance(node_check, str):
|
||||||
|
if current_node.tag != node_check:
|
||||||
|
return
|
||||||
|
elif node_check is not None:
|
||||||
|
if not isinstance(current_node, node_check):
|
||||||
|
return
|
||||||
|
if index_check is True and current_index is not None:
|
||||||
|
return
|
||||||
|
if (index_check is False or index_check is None) \
|
||||||
|
and current_index is None:
|
||||||
|
return
|
||||||
|
if isinstance(index_check, str):
|
||||||
|
if not (isinstance(current_index, ScalarNode)
|
||||||
|
and index_check == current_index.value):
|
||||||
|
return
|
||||||
|
elif isinstance(index_check, int) and not isinstance(index_check, bool):
|
||||||
|
if index_check != current_index:
|
||||||
|
return
|
||||||
|
return True
|
||||||
|
|
||||||
|
def resolve(self, kind, value, implicit):
|
||||||
|
if kind is ScalarNode and implicit[0]:
|
||||||
|
if value == '':
|
||||||
|
resolvers = self.yaml_implicit_resolvers.get('', [])
|
||||||
|
else:
|
||||||
|
resolvers = self.yaml_implicit_resolvers.get(value[0], [])
|
||||||
|
wildcard_resolvers = self.yaml_implicit_resolvers.get(None, [])
|
||||||
|
for tag, regexp in resolvers + wildcard_resolvers:
|
||||||
|
if regexp.match(value):
|
||||||
|
return tag
|
||||||
|
implicit = implicit[1]
|
||||||
|
if self.yaml_path_resolvers:
|
||||||
|
exact_paths = self.resolver_exact_paths[-1]
|
||||||
|
if kind in exact_paths:
|
||||||
|
return exact_paths[kind]
|
||||||
|
if None in exact_paths:
|
||||||
|
return exact_paths[None]
|
||||||
|
if kind is ScalarNode:
|
||||||
|
return self.DEFAULT_SCALAR_TAG
|
||||||
|
elif kind is SequenceNode:
|
||||||
|
return self.DEFAULT_SEQUENCE_TAG
|
||||||
|
elif kind is MappingNode:
|
||||||
|
return self.DEFAULT_MAPPING_TAG
|
||||||
|
|
||||||
|
class Resolver(BaseResolver):
|
||||||
|
pass
|
||||||
|
|
||||||
|
Resolver.add_implicit_resolver(
|
||||||
|
'tag:yaml.org,2002:bool',
|
||||||
|
re.compile(r'''^(?:yes|Yes|YES|no|No|NO
|
||||||
|
|true|True|TRUE|false|False|FALSE
|
||||||
|
|on|On|ON|off|Off|OFF)$''', re.X),
|
||||||
|
list('yYnNtTfFoO'))
|
||||||
|
|
||||||
|
Resolver.add_implicit_resolver(
|
||||||
|
'tag:yaml.org,2002:float',
|
||||||
|
re.compile(r'''^(?:[-+]?(?:[0-9][0-9_]*)\.[0-9_]*(?:[eE][-+][0-9]+)?
|
||||||
|
|\.[0-9][0-9_]*(?:[eE][-+][0-9]+)?
|
||||||
|
|[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+\.[0-9_]*
|
||||||
|
|[-+]?\.(?:inf|Inf|INF)
|
||||||
|
|\.(?:nan|NaN|NAN))$''', re.X),
|
||||||
|
list('-+0123456789.'))
|
||||||
|
|
||||||
|
Resolver.add_implicit_resolver(
|
||||||
|
'tag:yaml.org,2002:int',
|
||||||
|
re.compile(r'''^(?:[-+]?0b[0-1_]+
|
||||||
|
|[-+]?0[0-7_]+
|
||||||
|
|[-+]?(?:0|[1-9][0-9_]*)
|
||||||
|
|[-+]?0x[0-9a-fA-F_]+
|
||||||
|
|[-+]?[1-9][0-9_]*(?::[0-5]?[0-9])+)$''', re.X),
|
||||||
|
list('-+0123456789'))
|
||||||
|
|
||||||
|
Resolver.add_implicit_resolver(
|
||||||
|
'tag:yaml.org,2002:merge',
|
||||||
|
re.compile(r'^(?:<<)$'),
|
||||||
|
['<'])
|
||||||
|
|
||||||
|
Resolver.add_implicit_resolver(
|
||||||
|
'tag:yaml.org,2002:null',
|
||||||
|
re.compile(r'''^(?: ~
|
||||||
|
|null|Null|NULL
|
||||||
|
| )$''', re.X),
|
||||||
|
['~', 'n', 'N', ''])
|
||||||
|
|
||||||
|
Resolver.add_implicit_resolver(
|
||||||
|
'tag:yaml.org,2002:timestamp',
|
||||||
|
re.compile(r'''^(?:[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]
|
||||||
|
|[0-9][0-9][0-9][0-9] -[0-9][0-9]? -[0-9][0-9]?
|
||||||
|
(?:[Tt]|[ \t]+)[0-9][0-9]?
|
||||||
|
:[0-9][0-9] :[0-9][0-9] (?:\.[0-9]*)?
|
||||||
|
(?:[ \t]*(?:Z|[-+][0-9][0-9]?(?::[0-9][0-9])?))?)$''', re.X),
|
||||||
|
list('0123456789'))
|
||||||
|
|
||||||
|
Resolver.add_implicit_resolver(
|
||||||
|
'tag:yaml.org,2002:value',
|
||||||
|
re.compile(r'^(?:=)$'),
|
||||||
|
['='])
|
||||||
|
|
||||||
|
# The following resolver is only for documentation purposes. It cannot work
|
||||||
|
# because plain scalars cannot start with '!', '&', or '*'.
|
||||||
|
Resolver.add_implicit_resolver(
|
||||||
|
'tag:yaml.org,2002:yaml',
|
||||||
|
re.compile(r'^(?:!|&|\*)$'),
|
||||||
|
list('!&*'))
|
||||||
|
|
||||||
1435
lib/sunhpc/yaml/scanner.py
Normal file
1435
lib/sunhpc/yaml/scanner.py
Normal file
File diff suppressed because it is too large
Load Diff
111
lib/sunhpc/yaml/serializer.py
Normal file
111
lib/sunhpc/yaml/serializer.py
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
|
||||||
|
__all__ = ['Serializer', 'SerializerError']
|
||||||
|
|
||||||
|
from .error import YAMLError
|
||||||
|
from .events import *
|
||||||
|
from .nodes import *
|
||||||
|
|
||||||
|
class SerializerError(YAMLError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class Serializer:
|
||||||
|
|
||||||
|
ANCHOR_TEMPLATE = 'id%03d'
|
||||||
|
|
||||||
|
def __init__(self, encoding=None,
|
||||||
|
explicit_start=None, explicit_end=None, version=None, tags=None):
|
||||||
|
self.use_encoding = encoding
|
||||||
|
self.use_explicit_start = explicit_start
|
||||||
|
self.use_explicit_end = explicit_end
|
||||||
|
self.use_version = version
|
||||||
|
self.use_tags = tags
|
||||||
|
self.serialized_nodes = {}
|
||||||
|
self.anchors = {}
|
||||||
|
self.last_anchor_id = 0
|
||||||
|
self.closed = None
|
||||||
|
|
||||||
|
def open(self):
|
||||||
|
if self.closed is None:
|
||||||
|
self.emit(StreamStartEvent(encoding=self.use_encoding))
|
||||||
|
self.closed = False
|
||||||
|
elif self.closed:
|
||||||
|
raise SerializerError("serializer is closed")
|
||||||
|
else:
|
||||||
|
raise SerializerError("serializer is already opened")
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
if self.closed is None:
|
||||||
|
raise SerializerError("serializer is not opened")
|
||||||
|
elif not self.closed:
|
||||||
|
self.emit(StreamEndEvent())
|
||||||
|
self.closed = True
|
||||||
|
|
||||||
|
#def __del__(self):
|
||||||
|
# self.close()
|
||||||
|
|
||||||
|
def serialize(self, node):
|
||||||
|
if self.closed is None:
|
||||||
|
raise SerializerError("serializer is not opened")
|
||||||
|
elif self.closed:
|
||||||
|
raise SerializerError("serializer is closed")
|
||||||
|
self.emit(DocumentStartEvent(explicit=self.use_explicit_start,
|
||||||
|
version=self.use_version, tags=self.use_tags))
|
||||||
|
self.anchor_node(node)
|
||||||
|
self.serialize_node(node, None, None)
|
||||||
|
self.emit(DocumentEndEvent(explicit=self.use_explicit_end))
|
||||||
|
self.serialized_nodes = {}
|
||||||
|
self.anchors = {}
|
||||||
|
self.last_anchor_id = 0
|
||||||
|
|
||||||
|
def anchor_node(self, node):
|
||||||
|
if node in self.anchors:
|
||||||
|
if self.anchors[node] is None:
|
||||||
|
self.anchors[node] = self.generate_anchor(node)
|
||||||
|
else:
|
||||||
|
self.anchors[node] = None
|
||||||
|
if isinstance(node, SequenceNode):
|
||||||
|
for item in node.value:
|
||||||
|
self.anchor_node(item)
|
||||||
|
elif isinstance(node, MappingNode):
|
||||||
|
for key, value in node.value:
|
||||||
|
self.anchor_node(key)
|
||||||
|
self.anchor_node(value)
|
||||||
|
|
||||||
|
def generate_anchor(self, node):
|
||||||
|
self.last_anchor_id += 1
|
||||||
|
return self.ANCHOR_TEMPLATE % self.last_anchor_id
|
||||||
|
|
||||||
|
def serialize_node(self, node, parent, index):
|
||||||
|
alias = self.anchors[node]
|
||||||
|
if node in self.serialized_nodes:
|
||||||
|
self.emit(AliasEvent(alias))
|
||||||
|
else:
|
||||||
|
self.serialized_nodes[node] = True
|
||||||
|
self.descend_resolver(parent, index)
|
||||||
|
if isinstance(node, ScalarNode):
|
||||||
|
detected_tag = self.resolve(ScalarNode, node.value, (True, False))
|
||||||
|
default_tag = self.resolve(ScalarNode, node.value, (False, True))
|
||||||
|
implicit = (node.tag == detected_tag), (node.tag == default_tag)
|
||||||
|
self.emit(ScalarEvent(alias, node.tag, implicit, node.value,
|
||||||
|
style=node.style))
|
||||||
|
elif isinstance(node, SequenceNode):
|
||||||
|
implicit = (node.tag
|
||||||
|
== self.resolve(SequenceNode, node.value, True))
|
||||||
|
self.emit(SequenceStartEvent(alias, node.tag, implicit,
|
||||||
|
flow_style=node.flow_style))
|
||||||
|
index = 0
|
||||||
|
for item in node.value:
|
||||||
|
self.serialize_node(item, node, index)
|
||||||
|
index += 1
|
||||||
|
self.emit(SequenceEndEvent())
|
||||||
|
elif isinstance(node, MappingNode):
|
||||||
|
implicit = (node.tag
|
||||||
|
== self.resolve(MappingNode, node.value, True))
|
||||||
|
self.emit(MappingStartEvent(alias, node.tag, implicit,
|
||||||
|
flow_style=node.flow_style))
|
||||||
|
for key, value in node.value:
|
||||||
|
self.serialize_node(key, node, None)
|
||||||
|
self.serialize_node(value, node, key)
|
||||||
|
self.emit(MappingEndEvent())
|
||||||
|
self.ascend_resolver()
|
||||||
|
|
||||||
104
lib/sunhpc/yaml/tokens.py
Normal file
104
lib/sunhpc/yaml/tokens.py
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
|
||||||
|
class Token(object):
|
||||||
|
def __init__(self, start_mark, end_mark):
|
||||||
|
self.start_mark = start_mark
|
||||||
|
self.end_mark = end_mark
|
||||||
|
def __repr__(self):
|
||||||
|
attributes = [key for key in self.__dict__
|
||||||
|
if not key.endswith('_mark')]
|
||||||
|
attributes.sort()
|
||||||
|
arguments = ', '.join(['%s=%r' % (key, getattr(self, key))
|
||||||
|
for key in attributes])
|
||||||
|
return '%s(%s)' % (self.__class__.__name__, arguments)
|
||||||
|
|
||||||
|
#class BOMToken(Token):
|
||||||
|
# id = '<byte order mark>'
|
||||||
|
|
||||||
|
class DirectiveToken(Token):
|
||||||
|
id = '<directive>'
|
||||||
|
def __init__(self, name, value, start_mark, end_mark):
|
||||||
|
self.name = name
|
||||||
|
self.value = value
|
||||||
|
self.start_mark = start_mark
|
||||||
|
self.end_mark = end_mark
|
||||||
|
|
||||||
|
class DocumentStartToken(Token):
|
||||||
|
id = '<document start>'
|
||||||
|
|
||||||
|
class DocumentEndToken(Token):
|
||||||
|
id = '<document end>'
|
||||||
|
|
||||||
|
class StreamStartToken(Token):
|
||||||
|
id = '<stream start>'
|
||||||
|
def __init__(self, start_mark=None, end_mark=None,
|
||||||
|
encoding=None):
|
||||||
|
self.start_mark = start_mark
|
||||||
|
self.end_mark = end_mark
|
||||||
|
self.encoding = encoding
|
||||||
|
|
||||||
|
class StreamEndToken(Token):
|
||||||
|
id = '<stream end>'
|
||||||
|
|
||||||
|
class BlockSequenceStartToken(Token):
|
||||||
|
id = '<block sequence start>'
|
||||||
|
|
||||||
|
class BlockMappingStartToken(Token):
|
||||||
|
id = '<block mapping start>'
|
||||||
|
|
||||||
|
class BlockEndToken(Token):
|
||||||
|
id = '<block end>'
|
||||||
|
|
||||||
|
class FlowSequenceStartToken(Token):
|
||||||
|
id = '['
|
||||||
|
|
||||||
|
class FlowMappingStartToken(Token):
|
||||||
|
id = '{'
|
||||||
|
|
||||||
|
class FlowSequenceEndToken(Token):
|
||||||
|
id = ']'
|
||||||
|
|
||||||
|
class FlowMappingEndToken(Token):
|
||||||
|
id = '}'
|
||||||
|
|
||||||
|
class KeyToken(Token):
|
||||||
|
id = '?'
|
||||||
|
|
||||||
|
class ValueToken(Token):
|
||||||
|
id = ':'
|
||||||
|
|
||||||
|
class BlockEntryToken(Token):
|
||||||
|
id = '-'
|
||||||
|
|
||||||
|
class FlowEntryToken(Token):
|
||||||
|
id = ','
|
||||||
|
|
||||||
|
class AliasToken(Token):
|
||||||
|
id = '<alias>'
|
||||||
|
def __init__(self, value, start_mark, end_mark):
|
||||||
|
self.value = value
|
||||||
|
self.start_mark = start_mark
|
||||||
|
self.end_mark = end_mark
|
||||||
|
|
||||||
|
class AnchorToken(Token):
|
||||||
|
id = '<anchor>'
|
||||||
|
def __init__(self, value, start_mark, end_mark):
|
||||||
|
self.value = value
|
||||||
|
self.start_mark = start_mark
|
||||||
|
self.end_mark = end_mark
|
||||||
|
|
||||||
|
class TagToken(Token):
|
||||||
|
id = '<tag>'
|
||||||
|
def __init__(self, value, start_mark, end_mark):
|
||||||
|
self.value = value
|
||||||
|
self.start_mark = start_mark
|
||||||
|
self.end_mark = end_mark
|
||||||
|
|
||||||
|
class ScalarToken(Token):
|
||||||
|
id = '<scalar>'
|
||||||
|
def __init__(self, value, plain, start_mark, end_mark, style=None):
|
||||||
|
self.value = value
|
||||||
|
self.plain = plain
|
||||||
|
self.start_mark = start_mark
|
||||||
|
self.end_mark = end_mark
|
||||||
|
self.style = style
|
||||||
|
|
||||||
Reference in New Issue
Block a user