17db96d56Sopenharmony_ci"""Policy framework for the email package. 27db96d56Sopenharmony_ci 37db96d56Sopenharmony_ciAllows fine grained feature control of how the package parses and emits data. 47db96d56Sopenharmony_ci""" 57db96d56Sopenharmony_ci 67db96d56Sopenharmony_ciimport abc 77db96d56Sopenharmony_cifrom email import header 87db96d56Sopenharmony_cifrom email import charset as _charset 97db96d56Sopenharmony_cifrom email.utils import _has_surrogates 107db96d56Sopenharmony_ci 117db96d56Sopenharmony_ci__all__ = [ 127db96d56Sopenharmony_ci 'Policy', 137db96d56Sopenharmony_ci 'Compat32', 147db96d56Sopenharmony_ci 'compat32', 157db96d56Sopenharmony_ci ] 167db96d56Sopenharmony_ci 177db96d56Sopenharmony_ci 187db96d56Sopenharmony_ciclass _PolicyBase: 197db96d56Sopenharmony_ci 207db96d56Sopenharmony_ci """Policy Object basic framework. 217db96d56Sopenharmony_ci 227db96d56Sopenharmony_ci This class is useless unless subclassed. A subclass should define 237db96d56Sopenharmony_ci class attributes with defaults for any values that are to be 247db96d56Sopenharmony_ci managed by the Policy object. The constructor will then allow 257db96d56Sopenharmony_ci non-default values to be set for these attributes at instance 267db96d56Sopenharmony_ci creation time. The instance will be callable, taking these same 277db96d56Sopenharmony_ci attributes keyword arguments, and returning a new instance 287db96d56Sopenharmony_ci identical to the called instance except for those values changed 297db96d56Sopenharmony_ci by the keyword arguments. Instances may be added, yielding new 307db96d56Sopenharmony_ci instances with any non-default values from the right hand 317db96d56Sopenharmony_ci operand overriding those in the left hand operand. That is, 327db96d56Sopenharmony_ci 337db96d56Sopenharmony_ci A + B == A(<non-default values of B>) 347db96d56Sopenharmony_ci 357db96d56Sopenharmony_ci The repr of an instance can be used to reconstruct the object 367db96d56Sopenharmony_ci if and only if the repr of the values can be used to reconstruct 377db96d56Sopenharmony_ci those values. 387db96d56Sopenharmony_ci 397db96d56Sopenharmony_ci """ 407db96d56Sopenharmony_ci 417db96d56Sopenharmony_ci def __init__(self, **kw): 427db96d56Sopenharmony_ci """Create new Policy, possibly overriding some defaults. 437db96d56Sopenharmony_ci 447db96d56Sopenharmony_ci See class docstring for a list of overridable attributes. 457db96d56Sopenharmony_ci 467db96d56Sopenharmony_ci """ 477db96d56Sopenharmony_ci for name, value in kw.items(): 487db96d56Sopenharmony_ci if hasattr(self, name): 497db96d56Sopenharmony_ci super(_PolicyBase,self).__setattr__(name, value) 507db96d56Sopenharmony_ci else: 517db96d56Sopenharmony_ci raise TypeError( 527db96d56Sopenharmony_ci "{!r} is an invalid keyword argument for {}".format( 537db96d56Sopenharmony_ci name, self.__class__.__name__)) 547db96d56Sopenharmony_ci 557db96d56Sopenharmony_ci def __repr__(self): 567db96d56Sopenharmony_ci args = [ "{}={!r}".format(name, value) 577db96d56Sopenharmony_ci for name, value in self.__dict__.items() ] 587db96d56Sopenharmony_ci return "{}({})".format(self.__class__.__name__, ', '.join(args)) 597db96d56Sopenharmony_ci 607db96d56Sopenharmony_ci def clone(self, **kw): 617db96d56Sopenharmony_ci """Return a new instance with specified attributes changed. 627db96d56Sopenharmony_ci 637db96d56Sopenharmony_ci The new instance has the same attribute values as the current object, 647db96d56Sopenharmony_ci except for the changes passed in as keyword arguments. 657db96d56Sopenharmony_ci 667db96d56Sopenharmony_ci """ 677db96d56Sopenharmony_ci newpolicy = self.__class__.__new__(self.__class__) 687db96d56Sopenharmony_ci for attr, value in self.__dict__.items(): 697db96d56Sopenharmony_ci object.__setattr__(newpolicy, attr, value) 707db96d56Sopenharmony_ci for attr, value in kw.items(): 717db96d56Sopenharmony_ci if not hasattr(self, attr): 727db96d56Sopenharmony_ci raise TypeError( 737db96d56Sopenharmony_ci "{!r} is an invalid keyword argument for {}".format( 747db96d56Sopenharmony_ci attr, self.__class__.__name__)) 757db96d56Sopenharmony_ci object.__setattr__(newpolicy, attr, value) 767db96d56Sopenharmony_ci return newpolicy 777db96d56Sopenharmony_ci 787db96d56Sopenharmony_ci def __setattr__(self, name, value): 797db96d56Sopenharmony_ci if hasattr(self, name): 807db96d56Sopenharmony_ci msg = "{!r} object attribute {!r} is read-only" 817db96d56Sopenharmony_ci else: 827db96d56Sopenharmony_ci msg = "{!r} object has no attribute {!r}" 837db96d56Sopenharmony_ci raise AttributeError(msg.format(self.__class__.__name__, name)) 847db96d56Sopenharmony_ci 857db96d56Sopenharmony_ci def __add__(self, other): 867db96d56Sopenharmony_ci """Non-default values from right operand override those from left. 877db96d56Sopenharmony_ci 887db96d56Sopenharmony_ci The object returned is a new instance of the subclass. 897db96d56Sopenharmony_ci 907db96d56Sopenharmony_ci """ 917db96d56Sopenharmony_ci return self.clone(**other.__dict__) 927db96d56Sopenharmony_ci 937db96d56Sopenharmony_ci 947db96d56Sopenharmony_cidef _append_doc(doc, added_doc): 957db96d56Sopenharmony_ci doc = doc.rsplit('\n', 1)[0] 967db96d56Sopenharmony_ci added_doc = added_doc.split('\n', 1)[1] 977db96d56Sopenharmony_ci return doc + '\n' + added_doc 987db96d56Sopenharmony_ci 997db96d56Sopenharmony_cidef _extend_docstrings(cls): 1007db96d56Sopenharmony_ci if cls.__doc__ and cls.__doc__.startswith('+'): 1017db96d56Sopenharmony_ci cls.__doc__ = _append_doc(cls.__bases__[0].__doc__, cls.__doc__) 1027db96d56Sopenharmony_ci for name, attr in cls.__dict__.items(): 1037db96d56Sopenharmony_ci if attr.__doc__ and attr.__doc__.startswith('+'): 1047db96d56Sopenharmony_ci for c in (c for base in cls.__bases__ for c in base.mro()): 1057db96d56Sopenharmony_ci doc = getattr(getattr(c, name), '__doc__') 1067db96d56Sopenharmony_ci if doc: 1077db96d56Sopenharmony_ci attr.__doc__ = _append_doc(doc, attr.__doc__) 1087db96d56Sopenharmony_ci break 1097db96d56Sopenharmony_ci return cls 1107db96d56Sopenharmony_ci 1117db96d56Sopenharmony_ci 1127db96d56Sopenharmony_ciclass Policy(_PolicyBase, metaclass=abc.ABCMeta): 1137db96d56Sopenharmony_ci 1147db96d56Sopenharmony_ci r"""Controls for how messages are interpreted and formatted. 1157db96d56Sopenharmony_ci 1167db96d56Sopenharmony_ci Most of the classes and many of the methods in the email package accept 1177db96d56Sopenharmony_ci Policy objects as parameters. A Policy object contains a set of values and 1187db96d56Sopenharmony_ci functions that control how input is interpreted and how output is rendered. 1197db96d56Sopenharmony_ci For example, the parameter 'raise_on_defect' controls whether or not an RFC 1207db96d56Sopenharmony_ci violation results in an error being raised or not, while 'max_line_length' 1217db96d56Sopenharmony_ci controls the maximum length of output lines when a Message is serialized. 1227db96d56Sopenharmony_ci 1237db96d56Sopenharmony_ci Any valid attribute may be overridden when a Policy is created by passing 1247db96d56Sopenharmony_ci it as a keyword argument to the constructor. Policy objects are immutable, 1257db96d56Sopenharmony_ci but a new Policy object can be created with only certain values changed by 1267db96d56Sopenharmony_ci calling the Policy instance with keyword arguments. Policy objects can 1277db96d56Sopenharmony_ci also be added, producing a new Policy object in which the non-default 1287db96d56Sopenharmony_ci attributes set in the right hand operand overwrite those specified in the 1297db96d56Sopenharmony_ci left operand. 1307db96d56Sopenharmony_ci 1317db96d56Sopenharmony_ci Settable attributes: 1327db96d56Sopenharmony_ci 1337db96d56Sopenharmony_ci raise_on_defect -- If true, then defects should be raised as errors. 1347db96d56Sopenharmony_ci Default: False. 1357db96d56Sopenharmony_ci 1367db96d56Sopenharmony_ci linesep -- string containing the value to use as separation 1377db96d56Sopenharmony_ci between output lines. Default '\n'. 1387db96d56Sopenharmony_ci 1397db96d56Sopenharmony_ci cte_type -- Type of allowed content transfer encodings 1407db96d56Sopenharmony_ci 1417db96d56Sopenharmony_ci 7bit -- ASCII only 1427db96d56Sopenharmony_ci 8bit -- Content-Transfer-Encoding: 8bit is allowed 1437db96d56Sopenharmony_ci 1447db96d56Sopenharmony_ci Default: 8bit. Also controls the disposition of 1457db96d56Sopenharmony_ci (RFC invalid) binary data in headers; see the 1467db96d56Sopenharmony_ci documentation of the binary_fold method. 1477db96d56Sopenharmony_ci 1487db96d56Sopenharmony_ci max_line_length -- maximum length of lines, excluding 'linesep', 1497db96d56Sopenharmony_ci during serialization. None or 0 means no line 1507db96d56Sopenharmony_ci wrapping is done. Default is 78. 1517db96d56Sopenharmony_ci 1527db96d56Sopenharmony_ci mangle_from_ -- a flag that, when True escapes From_ lines in the 1537db96d56Sopenharmony_ci body of the message by putting a `>' in front of 1547db96d56Sopenharmony_ci them. This is used when the message is being 1557db96d56Sopenharmony_ci serialized by a generator. Default: True. 1567db96d56Sopenharmony_ci 1577db96d56Sopenharmony_ci message_factory -- the class to use to create new message objects. 1587db96d56Sopenharmony_ci If the value is None, the default is Message. 1597db96d56Sopenharmony_ci 1607db96d56Sopenharmony_ci """ 1617db96d56Sopenharmony_ci 1627db96d56Sopenharmony_ci raise_on_defect = False 1637db96d56Sopenharmony_ci linesep = '\n' 1647db96d56Sopenharmony_ci cte_type = '8bit' 1657db96d56Sopenharmony_ci max_line_length = 78 1667db96d56Sopenharmony_ci mangle_from_ = False 1677db96d56Sopenharmony_ci message_factory = None 1687db96d56Sopenharmony_ci 1697db96d56Sopenharmony_ci def handle_defect(self, obj, defect): 1707db96d56Sopenharmony_ci """Based on policy, either raise defect or call register_defect. 1717db96d56Sopenharmony_ci 1727db96d56Sopenharmony_ci handle_defect(obj, defect) 1737db96d56Sopenharmony_ci 1747db96d56Sopenharmony_ci defect should be a Defect subclass, but in any case must be an 1757db96d56Sopenharmony_ci Exception subclass. obj is the object on which the defect should be 1767db96d56Sopenharmony_ci registered if it is not raised. If the raise_on_defect is True, the 1777db96d56Sopenharmony_ci defect is raised as an error, otherwise the object and the defect are 1787db96d56Sopenharmony_ci passed to register_defect. 1797db96d56Sopenharmony_ci 1807db96d56Sopenharmony_ci This method is intended to be called by parsers that discover defects. 1817db96d56Sopenharmony_ci The email package parsers always call it with Defect instances. 1827db96d56Sopenharmony_ci 1837db96d56Sopenharmony_ci """ 1847db96d56Sopenharmony_ci if self.raise_on_defect: 1857db96d56Sopenharmony_ci raise defect 1867db96d56Sopenharmony_ci self.register_defect(obj, defect) 1877db96d56Sopenharmony_ci 1887db96d56Sopenharmony_ci def register_defect(self, obj, defect): 1897db96d56Sopenharmony_ci """Record 'defect' on 'obj'. 1907db96d56Sopenharmony_ci 1917db96d56Sopenharmony_ci Called by handle_defect if raise_on_defect is False. This method is 1927db96d56Sopenharmony_ci part of the Policy API so that Policy subclasses can implement custom 1937db96d56Sopenharmony_ci defect handling. The default implementation calls the append method of 1947db96d56Sopenharmony_ci the defects attribute of obj. The objects used by the email package by 1957db96d56Sopenharmony_ci default that get passed to this method will always have a defects 1967db96d56Sopenharmony_ci attribute with an append method. 1977db96d56Sopenharmony_ci 1987db96d56Sopenharmony_ci """ 1997db96d56Sopenharmony_ci obj.defects.append(defect) 2007db96d56Sopenharmony_ci 2017db96d56Sopenharmony_ci def header_max_count(self, name): 2027db96d56Sopenharmony_ci """Return the maximum allowed number of headers named 'name'. 2037db96d56Sopenharmony_ci 2047db96d56Sopenharmony_ci Called when a header is added to a Message object. If the returned 2057db96d56Sopenharmony_ci value is not 0 or None, and there are already a number of headers with 2067db96d56Sopenharmony_ci the name 'name' equal to the value returned, a ValueError is raised. 2077db96d56Sopenharmony_ci 2087db96d56Sopenharmony_ci Because the default behavior of Message's __setitem__ is to append the 2097db96d56Sopenharmony_ci value to the list of headers, it is easy to create duplicate headers 2107db96d56Sopenharmony_ci without realizing it. This method allows certain headers to be limited 2117db96d56Sopenharmony_ci in the number of instances of that header that may be added to a 2127db96d56Sopenharmony_ci Message programmatically. (The limit is not observed by the parser, 2137db96d56Sopenharmony_ci which will faithfully produce as many headers as exist in the message 2147db96d56Sopenharmony_ci being parsed.) 2157db96d56Sopenharmony_ci 2167db96d56Sopenharmony_ci The default implementation returns None for all header names. 2177db96d56Sopenharmony_ci """ 2187db96d56Sopenharmony_ci return None 2197db96d56Sopenharmony_ci 2207db96d56Sopenharmony_ci @abc.abstractmethod 2217db96d56Sopenharmony_ci def header_source_parse(self, sourcelines): 2227db96d56Sopenharmony_ci """Given a list of linesep terminated strings constituting the lines of 2237db96d56Sopenharmony_ci a single header, return the (name, value) tuple that should be stored 2247db96d56Sopenharmony_ci in the model. The input lines should retain their terminating linesep 2257db96d56Sopenharmony_ci characters. The lines passed in by the email package may contain 2267db96d56Sopenharmony_ci surrogateescaped binary data. 2277db96d56Sopenharmony_ci """ 2287db96d56Sopenharmony_ci raise NotImplementedError 2297db96d56Sopenharmony_ci 2307db96d56Sopenharmony_ci @abc.abstractmethod 2317db96d56Sopenharmony_ci def header_store_parse(self, name, value): 2327db96d56Sopenharmony_ci """Given the header name and the value provided by the application 2337db96d56Sopenharmony_ci program, return the (name, value) that should be stored in the model. 2347db96d56Sopenharmony_ci """ 2357db96d56Sopenharmony_ci raise NotImplementedError 2367db96d56Sopenharmony_ci 2377db96d56Sopenharmony_ci @abc.abstractmethod 2387db96d56Sopenharmony_ci def header_fetch_parse(self, name, value): 2397db96d56Sopenharmony_ci """Given the header name and the value from the model, return the value 2407db96d56Sopenharmony_ci to be returned to the application program that is requesting that 2417db96d56Sopenharmony_ci header. The value passed in by the email package may contain 2427db96d56Sopenharmony_ci surrogateescaped binary data if the lines were parsed by a BytesParser. 2437db96d56Sopenharmony_ci The returned value should not contain any surrogateescaped data. 2447db96d56Sopenharmony_ci 2457db96d56Sopenharmony_ci """ 2467db96d56Sopenharmony_ci raise NotImplementedError 2477db96d56Sopenharmony_ci 2487db96d56Sopenharmony_ci @abc.abstractmethod 2497db96d56Sopenharmony_ci def fold(self, name, value): 2507db96d56Sopenharmony_ci """Given the header name and the value from the model, return a string 2517db96d56Sopenharmony_ci containing linesep characters that implement the folding of the header 2527db96d56Sopenharmony_ci according to the policy controls. The value passed in by the email 2537db96d56Sopenharmony_ci package may contain surrogateescaped binary data if the lines were 2547db96d56Sopenharmony_ci parsed by a BytesParser. The returned value should not contain any 2557db96d56Sopenharmony_ci surrogateescaped data. 2567db96d56Sopenharmony_ci 2577db96d56Sopenharmony_ci """ 2587db96d56Sopenharmony_ci raise NotImplementedError 2597db96d56Sopenharmony_ci 2607db96d56Sopenharmony_ci @abc.abstractmethod 2617db96d56Sopenharmony_ci def fold_binary(self, name, value): 2627db96d56Sopenharmony_ci """Given the header name and the value from the model, return binary 2637db96d56Sopenharmony_ci data containing linesep characters that implement the folding of the 2647db96d56Sopenharmony_ci header according to the policy controls. The value passed in by the 2657db96d56Sopenharmony_ci email package may contain surrogateescaped binary data. 2667db96d56Sopenharmony_ci 2677db96d56Sopenharmony_ci """ 2687db96d56Sopenharmony_ci raise NotImplementedError 2697db96d56Sopenharmony_ci 2707db96d56Sopenharmony_ci 2717db96d56Sopenharmony_ci@_extend_docstrings 2727db96d56Sopenharmony_ciclass Compat32(Policy): 2737db96d56Sopenharmony_ci 2747db96d56Sopenharmony_ci """+ 2757db96d56Sopenharmony_ci This particular policy is the backward compatibility Policy. It 2767db96d56Sopenharmony_ci replicates the behavior of the email package version 5.1. 2777db96d56Sopenharmony_ci """ 2787db96d56Sopenharmony_ci 2797db96d56Sopenharmony_ci mangle_from_ = True 2807db96d56Sopenharmony_ci 2817db96d56Sopenharmony_ci def _sanitize_header(self, name, value): 2827db96d56Sopenharmony_ci # If the header value contains surrogates, return a Header using 2837db96d56Sopenharmony_ci # the unknown-8bit charset to encode the bytes as encoded words. 2847db96d56Sopenharmony_ci if not isinstance(value, str): 2857db96d56Sopenharmony_ci # Assume it is already a header object 2867db96d56Sopenharmony_ci return value 2877db96d56Sopenharmony_ci if _has_surrogates(value): 2887db96d56Sopenharmony_ci return header.Header(value, charset=_charset.UNKNOWN8BIT, 2897db96d56Sopenharmony_ci header_name=name) 2907db96d56Sopenharmony_ci else: 2917db96d56Sopenharmony_ci return value 2927db96d56Sopenharmony_ci 2937db96d56Sopenharmony_ci def header_source_parse(self, sourcelines): 2947db96d56Sopenharmony_ci """+ 2957db96d56Sopenharmony_ci The name is parsed as everything up to the ':' and returned unmodified. 2967db96d56Sopenharmony_ci The value is determined by stripping leading whitespace off the 2977db96d56Sopenharmony_ci remainder of the first line, joining all subsequent lines together, and 2987db96d56Sopenharmony_ci stripping any trailing carriage return or linefeed characters. 2997db96d56Sopenharmony_ci 3007db96d56Sopenharmony_ci """ 3017db96d56Sopenharmony_ci name, value = sourcelines[0].split(':', 1) 3027db96d56Sopenharmony_ci value = value.lstrip(' \t') + ''.join(sourcelines[1:]) 3037db96d56Sopenharmony_ci return (name, value.rstrip('\r\n')) 3047db96d56Sopenharmony_ci 3057db96d56Sopenharmony_ci def header_store_parse(self, name, value): 3067db96d56Sopenharmony_ci """+ 3077db96d56Sopenharmony_ci The name and value are returned unmodified. 3087db96d56Sopenharmony_ci """ 3097db96d56Sopenharmony_ci return (name, value) 3107db96d56Sopenharmony_ci 3117db96d56Sopenharmony_ci def header_fetch_parse(self, name, value): 3127db96d56Sopenharmony_ci """+ 3137db96d56Sopenharmony_ci If the value contains binary data, it is converted into a Header object 3147db96d56Sopenharmony_ci using the unknown-8bit charset. Otherwise it is returned unmodified. 3157db96d56Sopenharmony_ci """ 3167db96d56Sopenharmony_ci return self._sanitize_header(name, value) 3177db96d56Sopenharmony_ci 3187db96d56Sopenharmony_ci def fold(self, name, value): 3197db96d56Sopenharmony_ci """+ 3207db96d56Sopenharmony_ci Headers are folded using the Header folding algorithm, which preserves 3217db96d56Sopenharmony_ci existing line breaks in the value, and wraps each resulting line to the 3227db96d56Sopenharmony_ci max_line_length. Non-ASCII binary data are CTE encoded using the 3237db96d56Sopenharmony_ci unknown-8bit charset. 3247db96d56Sopenharmony_ci 3257db96d56Sopenharmony_ci """ 3267db96d56Sopenharmony_ci return self._fold(name, value, sanitize=True) 3277db96d56Sopenharmony_ci 3287db96d56Sopenharmony_ci def fold_binary(self, name, value): 3297db96d56Sopenharmony_ci """+ 3307db96d56Sopenharmony_ci Headers are folded using the Header folding algorithm, which preserves 3317db96d56Sopenharmony_ci existing line breaks in the value, and wraps each resulting line to the 3327db96d56Sopenharmony_ci max_line_length. If cte_type is 7bit, non-ascii binary data is CTE 3337db96d56Sopenharmony_ci encoded using the unknown-8bit charset. Otherwise the original source 3347db96d56Sopenharmony_ci header is used, with its existing line breaks and/or binary data. 3357db96d56Sopenharmony_ci 3367db96d56Sopenharmony_ci """ 3377db96d56Sopenharmony_ci folded = self._fold(name, value, sanitize=self.cte_type=='7bit') 3387db96d56Sopenharmony_ci return folded.encode('ascii', 'surrogateescape') 3397db96d56Sopenharmony_ci 3407db96d56Sopenharmony_ci def _fold(self, name, value, sanitize): 3417db96d56Sopenharmony_ci parts = [] 3427db96d56Sopenharmony_ci parts.append('%s: ' % name) 3437db96d56Sopenharmony_ci if isinstance(value, str): 3447db96d56Sopenharmony_ci if _has_surrogates(value): 3457db96d56Sopenharmony_ci if sanitize: 3467db96d56Sopenharmony_ci h = header.Header(value, 3477db96d56Sopenharmony_ci charset=_charset.UNKNOWN8BIT, 3487db96d56Sopenharmony_ci header_name=name) 3497db96d56Sopenharmony_ci else: 3507db96d56Sopenharmony_ci # If we have raw 8bit data in a byte string, we have no idea 3517db96d56Sopenharmony_ci # what the encoding is. There is no safe way to split this 3527db96d56Sopenharmony_ci # string. If it's ascii-subset, then we could do a normal 3537db96d56Sopenharmony_ci # ascii split, but if it's multibyte then we could break the 3547db96d56Sopenharmony_ci # string. There's no way to know so the least harm seems to 3557db96d56Sopenharmony_ci # be to not split the string and risk it being too long. 3567db96d56Sopenharmony_ci parts.append(value) 3577db96d56Sopenharmony_ci h = None 3587db96d56Sopenharmony_ci else: 3597db96d56Sopenharmony_ci h = header.Header(value, header_name=name) 3607db96d56Sopenharmony_ci else: 3617db96d56Sopenharmony_ci # Assume it is a Header-like object. 3627db96d56Sopenharmony_ci h = value 3637db96d56Sopenharmony_ci if h is not None: 3647db96d56Sopenharmony_ci # The Header class interprets a value of None for maxlinelen as the 3657db96d56Sopenharmony_ci # default value of 78, as recommended by RFC 2822. 3667db96d56Sopenharmony_ci maxlinelen = 0 3677db96d56Sopenharmony_ci if self.max_line_length is not None: 3687db96d56Sopenharmony_ci maxlinelen = self.max_line_length 3697db96d56Sopenharmony_ci parts.append(h.encode(linesep=self.linesep, maxlinelen=maxlinelen)) 3707db96d56Sopenharmony_ci parts.append(self.linesep) 3717db96d56Sopenharmony_ci return ''.join(parts) 3727db96d56Sopenharmony_ci 3737db96d56Sopenharmony_ci 3747db96d56Sopenharmony_cicompat32 = Compat32() 375