summaryrefslogtreecommitdiffstats
path: root/lib/ansible/parsing/yaml
diff options
context:
space:
mode:
Diffstat (limited to 'lib/ansible/parsing/yaml')
-rw-r--r--lib/ansible/parsing/yaml/__init__.py20
-rw-r--r--lib/ansible/parsing/yaml/constructor.py178
-rw-r--r--lib/ansible/parsing/yaml/dumper.py122
-rw-r--r--lib/ansible/parsing/yaml/loader.py45
-rw-r--r--lib/ansible/parsing/yaml/objects.py365
5 files changed, 730 insertions, 0 deletions
diff --git a/lib/ansible/parsing/yaml/__init__.py b/lib/ansible/parsing/yaml/__init__.py
new file mode 100644
index 0000000..ae8ccff
--- /dev/null
+++ b/lib/ansible/parsing/yaml/__init__.py
@@ -0,0 +1,20 @@
+# (c) 2012-2014, Michael DeHaan <michael.dehaan@gmail.com>
+#
+# This file is part of Ansible
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
+
+# Make coding more python3-ish
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
diff --git a/lib/ansible/parsing/yaml/constructor.py b/lib/ansible/parsing/yaml/constructor.py
new file mode 100644
index 0000000..4b79578
--- /dev/null
+++ b/lib/ansible/parsing/yaml/constructor.py
@@ -0,0 +1,178 @@
+# (c) 2012-2014, Michael DeHaan <michael.dehaan@gmail.com>
+#
+# This file is part of Ansible
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
+
+# Make coding more python3-ish
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+from yaml.constructor import SafeConstructor, ConstructorError
+from yaml.nodes import MappingNode
+
+from ansible import constants as C
+from ansible.module_utils._text import to_bytes, to_native
+from ansible.parsing.yaml.objects import AnsibleMapping, AnsibleSequence, AnsibleUnicode, AnsibleVaultEncryptedUnicode
+from ansible.parsing.vault import VaultLib
+from ansible.utils.display import Display
+from ansible.utils.unsafe_proxy import wrap_var
+
+display = Display()
+
+
+class AnsibleConstructor(SafeConstructor):
+ def __init__(self, file_name=None, vault_secrets=None):
+ self._ansible_file_name = file_name
+ super(AnsibleConstructor, self).__init__()
+ self._vaults = {}
+ self.vault_secrets = vault_secrets or []
+ self._vaults['default'] = VaultLib(secrets=self.vault_secrets)
+
+ def construct_yaml_map(self, node):
+ data = AnsibleMapping()
+ yield data
+ value = self.construct_mapping(node)
+ data.update(value)
+ data.ansible_pos = self._node_position_info(node)
+
+ def construct_mapping(self, node, deep=False):
+ # Most of this is from yaml.constructor.SafeConstructor. We replicate
+ # it here so that we can warn users when they have duplicate dict keys
+ # (pyyaml silently allows overwriting keys)
+ if not isinstance(node, MappingNode):
+ raise ConstructorError(None, None,
+ "expected a mapping node, but found %s" % node.id,
+ node.start_mark)
+ self.flatten_mapping(node)
+ mapping = AnsibleMapping()
+
+ # Add our extra information to the returned value
+ mapping.ansible_pos = self._node_position_info(node)
+
+ for key_node, value_node in node.value:
+ key = self.construct_object(key_node, deep=deep)
+ try:
+ hash(key)
+ except TypeError as exc:
+ raise ConstructorError("while constructing a mapping", node.start_mark,
+ "found unacceptable key (%s)" % exc, key_node.start_mark)
+
+ if key in mapping:
+ msg = (u'While constructing a mapping from {1}, line {2}, column {3}, found a duplicate dict key ({0}).'
+ u' Using last defined value only.'.format(key, *mapping.ansible_pos))
+ if C.DUPLICATE_YAML_DICT_KEY == 'warn':
+ display.warning(msg)
+ elif C.DUPLICATE_YAML_DICT_KEY == 'error':
+ raise ConstructorError(context=None, context_mark=None,
+ problem=to_native(msg),
+ problem_mark=node.start_mark,
+ note=None)
+ else:
+ # when 'ignore'
+ display.debug(msg)
+
+ value = self.construct_object(value_node, deep=deep)
+ mapping[key] = value
+
+ return mapping
+
+ def construct_yaml_str(self, node):
+ # Override the default string handling function
+ # to always return unicode objects
+ value = self.construct_scalar(node)
+ ret = AnsibleUnicode(value)
+
+ ret.ansible_pos = self._node_position_info(node)
+
+ return ret
+
+ def construct_vault_encrypted_unicode(self, node):
+ value = self.construct_scalar(node)
+ b_ciphertext_data = to_bytes(value)
+ # could pass in a key id here to choose the vault to associate with
+ # TODO/FIXME: plugin vault selector
+ vault = self._vaults['default']
+ if vault.secrets is None:
+ raise ConstructorError(context=None, context_mark=None,
+ problem="found !vault but no vault password provided",
+ problem_mark=node.start_mark,
+ note=None)
+ ret = AnsibleVaultEncryptedUnicode(b_ciphertext_data)
+ ret.vault = vault
+ ret.ansible_pos = self._node_position_info(node)
+ return ret
+
+ def construct_yaml_seq(self, node):
+ data = AnsibleSequence()
+ yield data
+ data.extend(self.construct_sequence(node))
+ data.ansible_pos = self._node_position_info(node)
+
+ def construct_yaml_unsafe(self, node):
+ try:
+ constructor = getattr(node, 'id', 'object')
+ if constructor is not None:
+ constructor = getattr(self, 'construct_%s' % constructor)
+ except AttributeError:
+ constructor = self.construct_object
+
+ value = constructor(node)
+
+ return wrap_var(value)
+
+ def _node_position_info(self, node):
+ # the line number where the previous token has ended (plus empty lines)
+ # Add one so that the first line is line 1 rather than line 0
+ column = node.start_mark.column + 1
+ line = node.start_mark.line + 1
+
+ # in some cases, we may have pre-read the data and then
+ # passed it to the load() call for YAML, in which case we
+ # want to override the default datasource (which would be
+ # '<string>') to the actual filename we read in
+ datasource = self._ansible_file_name or node.start_mark.name
+
+ return (datasource, line, column)
+
+
+AnsibleConstructor.add_constructor(
+ u'tag:yaml.org,2002:map',
+ AnsibleConstructor.construct_yaml_map)
+
+AnsibleConstructor.add_constructor(
+ u'tag:yaml.org,2002:python/dict',
+ AnsibleConstructor.construct_yaml_map)
+
+AnsibleConstructor.add_constructor(
+ u'tag:yaml.org,2002:str',
+ AnsibleConstructor.construct_yaml_str)
+
+AnsibleConstructor.add_constructor(
+ u'tag:yaml.org,2002:python/unicode',
+ AnsibleConstructor.construct_yaml_str)
+
+AnsibleConstructor.add_constructor(
+ u'tag:yaml.org,2002:seq',
+ AnsibleConstructor.construct_yaml_seq)
+
+AnsibleConstructor.add_constructor(
+ u'!unsafe',
+ AnsibleConstructor.construct_yaml_unsafe)
+
+AnsibleConstructor.add_constructor(
+ u'!vault',
+ AnsibleConstructor.construct_vault_encrypted_unicode)
+
+AnsibleConstructor.add_constructor(u'!vault-encrypted', AnsibleConstructor.construct_vault_encrypted_unicode)
diff --git a/lib/ansible/parsing/yaml/dumper.py b/lib/ansible/parsing/yaml/dumper.py
new file mode 100644
index 0000000..8701bb8
--- /dev/null
+++ b/lib/ansible/parsing/yaml/dumper.py
@@ -0,0 +1,122 @@
+# (c) 2012-2014, Michael DeHaan <michael.dehaan@gmail.com>
+#
+# This file is part of Ansible
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
+
+# Make coding more python3-ish
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+import yaml
+
+from ansible.module_utils.six import text_type, binary_type
+from ansible.module_utils.common.yaml import SafeDumper
+from ansible.parsing.yaml.objects import AnsibleUnicode, AnsibleSequence, AnsibleMapping, AnsibleVaultEncryptedUnicode
+from ansible.utils.unsafe_proxy import AnsibleUnsafeText, AnsibleUnsafeBytes, NativeJinjaUnsafeText, NativeJinjaText
+from ansible.template import AnsibleUndefined
+from ansible.vars.hostvars import HostVars, HostVarsVars
+from ansible.vars.manager import VarsWithSources
+
+
+class AnsibleDumper(SafeDumper):
+ '''
+ A simple stub class that allows us to add representers
+ for our overridden object types.
+ '''
+
+
+def represent_hostvars(self, data):
+ return self.represent_dict(dict(data))
+
+
+# Note: only want to represent the encrypted data
+def represent_vault_encrypted_unicode(self, data):
+ return self.represent_scalar(u'!vault', data._ciphertext.decode(), style='|')
+
+
+def represent_unicode(self, data):
+ return yaml.representer.SafeRepresenter.represent_str(self, text_type(data))
+
+
+def represent_binary(self, data):
+ return yaml.representer.SafeRepresenter.represent_binary(self, binary_type(data))
+
+
+def represent_undefined(self, data):
+ # Here bool will ensure _fail_with_undefined_error happens
+ # if the value is Undefined.
+ # This happens because Jinja sets __bool__ on StrictUndefined
+ return bool(data)
+
+
+AnsibleDumper.add_representer(
+ AnsibleUnicode,
+ represent_unicode,
+)
+
+AnsibleDumper.add_representer(
+ AnsibleUnsafeText,
+ represent_unicode,
+)
+
+AnsibleDumper.add_representer(
+ AnsibleUnsafeBytes,
+ represent_binary,
+)
+
+AnsibleDumper.add_representer(
+ HostVars,
+ represent_hostvars,
+)
+
+AnsibleDumper.add_representer(
+ HostVarsVars,
+ represent_hostvars,
+)
+
+AnsibleDumper.add_representer(
+ VarsWithSources,
+ represent_hostvars,
+)
+
+AnsibleDumper.add_representer(
+ AnsibleSequence,
+ yaml.representer.SafeRepresenter.represent_list,
+)
+
+AnsibleDumper.add_representer(
+ AnsibleMapping,
+ yaml.representer.SafeRepresenter.represent_dict,
+)
+
+AnsibleDumper.add_representer(
+ AnsibleVaultEncryptedUnicode,
+ represent_vault_encrypted_unicode,
+)
+
+AnsibleDumper.add_representer(
+ AnsibleUndefined,
+ represent_undefined,
+)
+
+AnsibleDumper.add_representer(
+ NativeJinjaUnsafeText,
+ represent_unicode,
+)
+
+AnsibleDumper.add_representer(
+ NativeJinjaText,
+ represent_unicode,
+)
diff --git a/lib/ansible/parsing/yaml/loader.py b/lib/ansible/parsing/yaml/loader.py
new file mode 100644
index 0000000..15bde79
--- /dev/null
+++ b/lib/ansible/parsing/yaml/loader.py
@@ -0,0 +1,45 @@
+# (c) 2012-2014, Michael DeHaan <michael.dehaan@gmail.com>
+#
+# This file is part of Ansible
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
+
+# Make coding more python3-ish
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+from yaml.resolver import Resolver
+
+from ansible.parsing.yaml.constructor import AnsibleConstructor
+from ansible.module_utils.common.yaml import HAS_LIBYAML, Parser
+
+if HAS_LIBYAML:
+ class AnsibleLoader(Parser, AnsibleConstructor, Resolver): # type: ignore[misc] # pylint: disable=inconsistent-mro
+ def __init__(self, stream, file_name=None, vault_secrets=None):
+ Parser.__init__(self, stream)
+ AnsibleConstructor.__init__(self, file_name=file_name, vault_secrets=vault_secrets)
+ Resolver.__init__(self)
+else:
+ from yaml.composer import Composer
+ from yaml.reader import Reader
+ from yaml.scanner import Scanner
+
+ class AnsibleLoader(Reader, Scanner, Parser, Composer, AnsibleConstructor, Resolver): # type: ignore[misc,no-redef] # pylint: disable=inconsistent-mro
+ def __init__(self, stream, file_name=None, vault_secrets=None):
+ Reader.__init__(self, stream)
+ Scanner.__init__(self)
+ Parser.__init__(self)
+ Composer.__init__(self)
+ AnsibleConstructor.__init__(self, file_name=file_name, vault_secrets=vault_secrets)
+ Resolver.__init__(self)
diff --git a/lib/ansible/parsing/yaml/objects.py b/lib/ansible/parsing/yaml/objects.py
new file mode 100644
index 0000000..a2e2a66
--- /dev/null
+++ b/lib/ansible/parsing/yaml/objects.py
@@ -0,0 +1,365 @@
+# (c) 2012-2014, Michael DeHaan <michael.dehaan@gmail.com>
+#
+# This file is part of Ansible
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
+
+# Make coding more python3-ish
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+import string
+import sys as _sys
+
+from collections.abc import Sequence
+
+import sys
+import yaml
+
+from ansible.module_utils.six import text_type
+from ansible.module_utils._text import to_bytes, to_text, to_native
+
+
+class AnsibleBaseYAMLObject(object):
+ '''
+ the base class used to sub-class python built-in objects
+ so that we can add attributes to them during yaml parsing
+
+ '''
+ _data_source = None
+ _line_number = 0
+ _column_number = 0
+
+ def _get_ansible_position(self):
+ return (self._data_source, self._line_number, self._column_number)
+
+ def _set_ansible_position(self, obj):
+ try:
+ (src, line, col) = obj
+ except (TypeError, ValueError):
+ raise AssertionError(
+ 'ansible_pos can only be set with a tuple/list '
+ 'of three values: source, line number, column number'
+ )
+ self._data_source = src
+ self._line_number = line
+ self._column_number = col
+
+ ansible_pos = property(_get_ansible_position, _set_ansible_position)
+
+
+class AnsibleMapping(AnsibleBaseYAMLObject, dict):
+ ''' sub class for dictionaries '''
+ pass
+
+
+class AnsibleUnicode(AnsibleBaseYAMLObject, text_type):
+ ''' sub class for unicode objects '''
+ pass
+
+
+class AnsibleSequence(AnsibleBaseYAMLObject, list):
+ ''' sub class for lists '''
+ pass
+
+
+class AnsibleVaultEncryptedUnicode(Sequence, AnsibleBaseYAMLObject):
+ '''Unicode like object that is not evaluated (decrypted) until it needs to be'''
+ __UNSAFE__ = True
+ __ENCRYPTED__ = True
+ yaml_tag = u'!vault'
+
+ @classmethod
+ def from_plaintext(cls, seq, vault, secret):
+ if not vault:
+ raise vault.AnsibleVaultError('Error creating AnsibleVaultEncryptedUnicode, invalid vault (%s) provided' % vault)
+
+ ciphertext = vault.encrypt(seq, secret)
+ avu = cls(ciphertext)
+ avu.vault = vault
+ return avu
+
+ def __init__(self, ciphertext):
+ '''A AnsibleUnicode with a Vault attribute that can decrypt it.
+
+ ciphertext is a byte string (str on PY2, bytestring on PY3).
+
+ The .data attribute is a property that returns the decrypted plaintext
+ of the ciphertext as a PY2 unicode or PY3 string object.
+ '''
+ super(AnsibleVaultEncryptedUnicode, self).__init__()
+
+ # after construction, calling code has to set the .vault attribute to a vaultlib object
+ self.vault = None
+ self._ciphertext = to_bytes(ciphertext)
+
+ @property
+ def data(self):
+ if not self.vault:
+ return to_text(self._ciphertext)
+ return to_text(self.vault.decrypt(self._ciphertext, obj=self))
+
+ @data.setter
+ def data(self, value):
+ self._ciphertext = to_bytes(value)
+
+ def is_encrypted(self):
+ return self.vault and self.vault.is_encrypted(self._ciphertext)
+
+ def __eq__(self, other):
+ if self.vault:
+ return other == self.data
+ return False
+
+ def __ne__(self, other):
+ if self.vault:
+ return other != self.data
+ return True
+
+ def __reversed__(self):
+ # This gets inerhited from ``collections.Sequence`` which returns a generator
+ # make this act more like the string implementation
+ return to_text(self[::-1], errors='surrogate_or_strict')
+
+ def __str__(self):
+ return to_native(self.data, errors='surrogate_or_strict')
+
+ def __unicode__(self):
+ return to_text(self.data, errors='surrogate_or_strict')
+
+ def encode(self, encoding=None, errors=None):
+ return to_bytes(self.data, encoding=encoding, errors=errors)
+
+ # Methods below are a copy from ``collections.UserString``
+ # Some are copied as is, where others are modified to not
+ # auto wrap with ``self.__class__``
+ def __repr__(self):
+ return repr(self.data)
+
+ def __int__(self, base=10):
+ return int(self.data, base=base)
+
+ def __float__(self):
+ return float(self.data)
+
+ def __complex__(self):
+ return complex(self.data)
+
+ def __hash__(self):
+ return hash(self.data)
+
+ # This breaks vault, do not define it, we cannot satisfy this
+ # def __getnewargs__(self):
+ # return (self.data[:],)
+
+ def __lt__(self, string):
+ if isinstance(string, AnsibleVaultEncryptedUnicode):
+ return self.data < string.data
+ return self.data < string
+
+ def __le__(self, string):
+ if isinstance(string, AnsibleVaultEncryptedUnicode):
+ return self.data <= string.data
+ return self.data <= string
+
+ def __gt__(self, string):
+ if isinstance(string, AnsibleVaultEncryptedUnicode):
+ return self.data > string.data
+ return self.data > string
+
+ def __ge__(self, string):
+ if isinstance(string, AnsibleVaultEncryptedUnicode):
+ return self.data >= string.data
+ return self.data >= string
+
+ def __contains__(self, char):
+ if isinstance(char, AnsibleVaultEncryptedUnicode):
+ char = char.data
+ return char in self.data
+
+ def __len__(self):
+ return len(self.data)
+
+ def __getitem__(self, index):
+ return self.data[index]
+
+ def __getslice__(self, start, end):
+ start = max(start, 0)
+ end = max(end, 0)
+ return self.data[start:end]
+
+ def __add__(self, other):
+ if isinstance(other, AnsibleVaultEncryptedUnicode):
+ return self.data + other.data
+ elif isinstance(other, text_type):
+ return self.data + other
+ return self.data + to_text(other)
+
+ def __radd__(self, other):
+ if isinstance(other, text_type):
+ return other + self.data
+ return to_text(other) + self.data
+
+ def __mul__(self, n):
+ return self.data * n
+
+ __rmul__ = __mul__
+
+ def __mod__(self, args):
+ return self.data % args
+
+ def __rmod__(self, template):
+ return to_text(template) % self
+
+ # the following methods are defined in alphabetical order:
+ def capitalize(self):
+ return self.data.capitalize()
+
+ def casefold(self):
+ return self.data.casefold()
+
+ def center(self, width, *args):
+ return self.data.center(width, *args)
+
+ def count(self, sub, start=0, end=_sys.maxsize):
+ if isinstance(sub, AnsibleVaultEncryptedUnicode):
+ sub = sub.data
+ return self.data.count(sub, start, end)
+
+ def endswith(self, suffix, start=0, end=_sys.maxsize):
+ return self.data.endswith(suffix, start, end)
+
+ def expandtabs(self, tabsize=8):
+ return self.data.expandtabs(tabsize)
+
+ def find(self, sub, start=0, end=_sys.maxsize):
+ if isinstance(sub, AnsibleVaultEncryptedUnicode):
+ sub = sub.data
+ return self.data.find(sub, start, end)
+
+ def format(self, *args, **kwds):
+ return self.data.format(*args, **kwds)
+
+ def format_map(self, mapping):
+ return self.data.format_map(mapping)
+
+ def index(self, sub, start=0, end=_sys.maxsize):
+ return self.data.index(sub, start, end)
+
+ def isalpha(self):
+ return self.data.isalpha()
+
+ def isalnum(self):
+ return self.data.isalnum()
+
+ def isascii(self):
+ return self.data.isascii()
+
+ def isdecimal(self):
+ return self.data.isdecimal()
+
+ def isdigit(self):
+ return self.data.isdigit()
+
+ def isidentifier(self):
+ return self.data.isidentifier()
+
+ def islower(self):
+ return self.data.islower()
+
+ def isnumeric(self):
+ return self.data.isnumeric()
+
+ def isprintable(self):
+ return self.data.isprintable()
+
+ def isspace(self):
+ return self.data.isspace()
+
+ def istitle(self):
+ return self.data.istitle()
+
+ def isupper(self):
+ return self.data.isupper()
+
+ def join(self, seq):
+ return self.data.join(seq)
+
+ def ljust(self, width, *args):
+ return self.data.ljust(width, *args)
+
+ def lower(self):
+ return self.data.lower()
+
+ def lstrip(self, chars=None):
+ return self.data.lstrip(chars)
+
+ maketrans = str.maketrans
+
+ def partition(self, sep):
+ return self.data.partition(sep)
+
+ def replace(self, old, new, maxsplit=-1):
+ if isinstance(old, AnsibleVaultEncryptedUnicode):
+ old = old.data
+ if isinstance(new, AnsibleVaultEncryptedUnicode):
+ new = new.data
+ return self.data.replace(old, new, maxsplit)
+
+ def rfind(self, sub, start=0, end=_sys.maxsize):
+ if isinstance(sub, AnsibleVaultEncryptedUnicode):
+ sub = sub.data
+ return self.data.rfind(sub, start, end)
+
+ def rindex(self, sub, start=0, end=_sys.maxsize):
+ return self.data.rindex(sub, start, end)
+
+ def rjust(self, width, *args):
+ return self.data.rjust(width, *args)
+
+ def rpartition(self, sep):
+ return self.data.rpartition(sep)
+
+ def rstrip(self, chars=None):
+ return self.data.rstrip(chars)
+
+ def split(self, sep=None, maxsplit=-1):
+ return self.data.split(sep, maxsplit)
+
+ def rsplit(self, sep=None, maxsplit=-1):
+ return self.data.rsplit(sep, maxsplit)
+
+ def splitlines(self, keepends=False):
+ return self.data.splitlines(keepends)
+
+ def startswith(self, prefix, start=0, end=_sys.maxsize):
+ return self.data.startswith(prefix, start, end)
+
+ def strip(self, chars=None):
+ return self.data.strip(chars)
+
+ def swapcase(self):
+ return self.data.swapcase()
+
+ def title(self):
+ return self.data.title()
+
+ def translate(self, *args):
+ return self.data.translate(*args)
+
+ def upper(self):
+ return self.data.upper()
+
+ def zfill(self, width):
+ return self.data.zfill(width)