base cmd module finished
This commit is contained in:
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