Funktionierender Prototyp des Serious Games zur Vermittlung von Wissen zu Software-Engineering-Arbeitsmodellen.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

compat.py 17KB

1 year ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656
  1. # -*- test-case-name: twisted.test.test_compat -*-
  2. #
  3. # Copyright (c) Twisted Matrix Laboratories.
  4. # See LICENSE for details.
  5. """
  6. Compatibility module to provide backwards compatibility for useful Python
  7. features.
  8. This is mainly for use of internal Twisted code. We encourage you to use
  9. the latest version of Python directly from your code, if possible.
  10. @var unicode: The type of Unicode strings, C{unicode} on Python 2 and C{str}
  11. on Python 3.
  12. @var NativeStringIO: An in-memory file-like object that operates on the native
  13. string type (bytes in Python 2, unicode in Python 3).
  14. @var urllib_parse: a URL-parsing module (urlparse on Python 2, urllib.parse on
  15. Python 3)
  16. """
  17. import inspect
  18. import os
  19. import platform
  20. import socket
  21. import sys
  22. import urllib.parse as urllib_parse
  23. import warnings
  24. from collections.abc import Sequence
  25. from functools import reduce
  26. from html import escape
  27. from http import cookiejar as cookielib
  28. from io import IOBase, StringIO as NativeStringIO, TextIOBase
  29. from sys import intern
  30. from types import FrameType, MethodType as _MethodType
  31. from typing import Any, AnyStr, cast
  32. from urllib.parse import quote as urlquote, unquote as urlunquote
  33. from incremental import Version
  34. from twisted.python.deprecate import deprecated, deprecatedModuleAttribute
  35. if sys.version_info >= (3, 7, 0):
  36. _PY37PLUS = True
  37. else:
  38. _PY37PLUS = False
  39. if platform.python_implementation() == "PyPy":
  40. _PYPY = True
  41. else:
  42. _PYPY = False
  43. FileType = IOBase
  44. deprecatedModuleAttribute(
  45. Version("Twisted", 21, 2, 0),
  46. "Obsolete alias for io.IOBase",
  47. __name__,
  48. "FileType",
  49. )
  50. frozenset = frozenset
  51. deprecatedModuleAttribute(
  52. Version("Twisted", 21, 2, 0),
  53. "Obsolete alias for frozenset builtin type",
  54. __name__,
  55. "frozenset",
  56. )
  57. InstanceType = object
  58. deprecatedModuleAttribute(
  59. Version("Twisted", 21, 2, 0),
  60. "Old-style classes don't exist in Python 3",
  61. __name__,
  62. "InstanceType",
  63. )
  64. izip = zip
  65. deprecatedModuleAttribute(
  66. Version("Twisted", 21, 2, 0),
  67. "Obsolete alias for zip() builtin",
  68. __name__,
  69. "izip",
  70. )
  71. long = int
  72. deprecatedModuleAttribute(
  73. Version("Twisted", 21, 2, 0),
  74. "Obsolete alias for int builtin type",
  75. __name__,
  76. "long",
  77. )
  78. range = range
  79. deprecatedModuleAttribute(
  80. Version("Twisted", 21, 2, 0),
  81. "Obsolete alias for range() builtin",
  82. __name__,
  83. "range",
  84. )
  85. raw_input = input
  86. deprecatedModuleAttribute(
  87. Version("Twisted", 21, 2, 0),
  88. "Obsolete alias for input() builtin",
  89. __name__,
  90. "raw_input",
  91. )
  92. set = set
  93. deprecatedModuleAttribute(
  94. Version("Twisted", 21, 2, 0),
  95. "Obsolete alias for set builtin type",
  96. __name__,
  97. "set",
  98. )
  99. StringType = str
  100. deprecatedModuleAttribute(
  101. Version("Twisted", 21, 2, 0),
  102. "Obsolete alias for str builtin type",
  103. __name__,
  104. "StringType",
  105. )
  106. unichr = chr
  107. deprecatedModuleAttribute(
  108. Version("Twisted", 21, 2, 0),
  109. "Obsolete alias for chr() builtin",
  110. __name__,
  111. "unichr",
  112. )
  113. unicode = str
  114. deprecatedModuleAttribute(
  115. Version("Twisted", 21, 2, 0),
  116. "Obsolete alias for str builtin type",
  117. __name__,
  118. "unicode",
  119. )
  120. xrange = range
  121. deprecatedModuleAttribute(
  122. Version("Twisted", 21, 2, 0),
  123. "Obsolete alias for range() builtin",
  124. __name__,
  125. "xrange",
  126. )
  127. @deprecated(Version("Twisted", 21, 2, 0), replacement="d.items()")
  128. def iteritems(d):
  129. """
  130. Return an iterable of the items of C{d}.
  131. @type d: L{dict}
  132. @rtype: iterable
  133. """
  134. return d.items()
  135. @deprecated(Version("Twisted", 21, 2, 0), replacement="d.values()")
  136. def itervalues(d):
  137. """
  138. Return an iterable of the values of C{d}.
  139. @type d: L{dict}
  140. @rtype: iterable
  141. """
  142. return d.values()
  143. @deprecated(Version("Twisted", 21, 2, 0), replacement="list(d.items())")
  144. def items(d):
  145. """
  146. Return a list of the items of C{d}.
  147. @type d: L{dict}
  148. @rtype: L{list}
  149. """
  150. return list(d.items())
  151. def currentframe(n: int = 0) -> FrameType:
  152. """
  153. In Python 3, L{inspect.currentframe} does not take a stack-level argument.
  154. Restore that functionality from Python 2 so we don't have to re-implement
  155. the C{f_back}-walking loop in places where it's called.
  156. @param n: The number of stack levels above the caller to walk.
  157. @return: a frame, n levels up the stack from the caller.
  158. """
  159. f = inspect.currentframe()
  160. for x in range(n + 1):
  161. assert f is not None
  162. f = f.f_back
  163. assert f is not None
  164. return f
  165. def execfile(filename, globals, locals=None):
  166. """
  167. Execute a Python script in the given namespaces.
  168. Similar to the execfile builtin, but a namespace is mandatory, partly
  169. because that's a sensible thing to require, and because otherwise we'd
  170. have to do some frame hacking.
  171. This is a compatibility implementation for Python 3 porting, to avoid the
  172. use of the deprecated builtin C{execfile} function.
  173. """
  174. if locals is None:
  175. locals = globals
  176. with open(filename, "rb") as fin:
  177. source = fin.read()
  178. code = compile(source, filename, "exec")
  179. exec(code, globals, locals)
  180. # type note: Can't find a Comparable type, despite
  181. # https://github.com/python/typing/issues/59
  182. def cmp(a: object, b: object) -> int:
  183. """
  184. Compare two objects.
  185. Returns a negative number if C{a < b}, zero if they are equal, and a
  186. positive number if C{a > b}.
  187. """
  188. if a < b: # type: ignore[operator]
  189. return -1
  190. elif a == b:
  191. return 0
  192. else:
  193. return 1
  194. def comparable(klass):
  195. """
  196. Class decorator that ensures support for the special C{__cmp__} method.
  197. C{__eq__}, C{__lt__}, etc. methods are added to the class, relying on
  198. C{__cmp__} to implement their comparisons.
  199. """
  200. def __eq__(self: Any, other: object) -> bool:
  201. c = cast(bool, self.__cmp__(other))
  202. if c is NotImplemented:
  203. return c
  204. return c == 0
  205. def __ne__(self: Any, other: object) -> bool:
  206. c = cast(bool, self.__cmp__(other))
  207. if c is NotImplemented:
  208. return c
  209. return c != 0
  210. def __lt__(self: Any, other: object) -> bool:
  211. c = cast(bool, self.__cmp__(other))
  212. if c is NotImplemented:
  213. return c
  214. return c < 0
  215. def __le__(self: Any, other: object) -> bool:
  216. c = cast(bool, self.__cmp__(other))
  217. if c is NotImplemented:
  218. return c
  219. return c <= 0
  220. def __gt__(self: Any, other: object) -> bool:
  221. c = cast(bool, self.__cmp__(other))
  222. if c is NotImplemented:
  223. return c
  224. return c > 0
  225. def __ge__(self: Any, other: object) -> bool:
  226. c = cast(bool, self.__cmp__(other))
  227. if c is NotImplemented:
  228. return c
  229. return c >= 0
  230. klass.__lt__ = __lt__
  231. klass.__gt__ = __gt__
  232. klass.__le__ = __le__
  233. klass.__ge__ = __ge__
  234. klass.__eq__ = __eq__
  235. klass.__ne__ = __ne__
  236. return klass
  237. def ioType(fileIshObject, default=str):
  238. """
  239. Determine the type which will be returned from the given file object's
  240. read() and accepted by its write() method as an argument.
  241. In other words, determine whether the given file is 'opened in text mode'.
  242. @param fileIshObject: Any object, but ideally one which resembles a file.
  243. @type fileIshObject: L{object}
  244. @param default: A default value to return when the type of C{fileIshObject}
  245. cannot be determined.
  246. @type default: L{type}
  247. @return: There are 3 possible return values:
  248. 1. L{str}, if the file is unambiguously opened in text mode.
  249. 2. L{bytes}, if the file is unambiguously opened in binary mode.
  250. 3. The C{default} parameter, if the given type is not understood.
  251. @rtype: L{type}
  252. """
  253. if isinstance(fileIshObject, TextIOBase):
  254. # If it's for text I/O, then it's for text I/O.
  255. return str
  256. if isinstance(fileIshObject, IOBase):
  257. # If it's for I/O but it's _not_ for text I/O, it's for bytes I/O.
  258. return bytes
  259. encoding = getattr(fileIshObject, "encoding", None)
  260. import codecs
  261. if isinstance(fileIshObject, (codecs.StreamReader, codecs.StreamWriter)):
  262. # On StreamReaderWriter, the 'encoding' attribute has special meaning;
  263. # it is unambiguously text.
  264. if encoding:
  265. return str
  266. else:
  267. return bytes
  268. return default
  269. def nativeString(s: AnyStr) -> str:
  270. """
  271. Convert C{bytes} or C{str} to C{str} type, using ASCII encoding if
  272. conversion is necessary.
  273. @raise UnicodeError: The input string is not ASCII encodable/decodable.
  274. @raise TypeError: The input is neither C{bytes} nor C{str}.
  275. """
  276. if not isinstance(s, (bytes, str)):
  277. raise TypeError("%r is neither bytes nor str" % s)
  278. if isinstance(s, bytes):
  279. return s.decode("ascii")
  280. else:
  281. # Ensure we're limited to ASCII subset:
  282. s.encode("ascii")
  283. return s
  284. def _matchingString(constantString, inputString):
  285. """
  286. Some functions, such as C{os.path.join}, operate on string arguments which
  287. may be bytes or text, and wish to return a value of the same type. In
  288. those cases you may wish to have a string constant (in the case of
  289. C{os.path.join}, that constant would be C{os.path.sep}) involved in the
  290. parsing or processing, that must be of a matching type in order to use
  291. string operations on it. L{_matchingString} will take a constant string
  292. (either L{bytes} or L{str}) and convert it to the same type as the
  293. input string. C{constantString} should contain only characters from ASCII;
  294. to ensure this, it will be encoded or decoded regardless.
  295. @param constantString: A string literal used in processing.
  296. @type constantString: L{str} or L{bytes}
  297. @param inputString: A byte string or text string provided by the user.
  298. @type inputString: L{str} or L{bytes}
  299. @return: C{constantString} converted into the same type as C{inputString}
  300. @rtype: the type of C{inputString}
  301. """
  302. if isinstance(constantString, bytes):
  303. otherType = constantString.decode("ascii")
  304. else:
  305. otherType = constantString.encode("ascii")
  306. if type(constantString) == type(inputString):
  307. return constantString
  308. else:
  309. return otherType
  310. @deprecated(
  311. Version("Twisted", 21, 2, 0),
  312. replacement="raise exception.with_traceback(traceback)",
  313. )
  314. def reraise(exception, traceback):
  315. """
  316. Re-raise an exception, with an optional traceback.
  317. Re-raised exceptions will be mutated, with their C{__traceback__} attribute
  318. being set.
  319. @param exception: The exception instance.
  320. @param traceback: The traceback to use, or L{None} indicating a new
  321. traceback.
  322. """
  323. raise exception.with_traceback(traceback)
  324. def iterbytes(originalBytes):
  325. """
  326. Return an iterable wrapper for a C{bytes} object that provides the behavior
  327. of iterating over C{bytes} on Python 2.
  328. In particular, the results of iteration are the individual bytes (rather
  329. than integers as on Python 3).
  330. @param originalBytes: A C{bytes} object that will be wrapped.
  331. """
  332. for i in range(len(originalBytes)):
  333. yield originalBytes[i : i + 1]
  334. @deprecated(Version("Twisted", 21, 2, 0), replacement="b'%d'")
  335. def intToBytes(i: int) -> bytes:
  336. """
  337. Convert the given integer into C{bytes}, as ASCII-encoded Arab numeral.
  338. @param i: The C{int} to convert to C{bytes}.
  339. @rtype: C{bytes}
  340. """
  341. return b"%d" % (i,)
  342. def lazyByteSlice(object, offset=0, size=None):
  343. """
  344. Return a copy of the given bytes-like object.
  345. If an offset is given, the copy starts at that offset. If a size is
  346. given, the copy will only be of that length.
  347. @param object: C{bytes} to be copied.
  348. @param offset: C{int}, starting index of copy.
  349. @param size: Optional, if an C{int} is given limit the length of copy
  350. to this size.
  351. """
  352. view = memoryview(object)
  353. if size is None:
  354. return view[offset:]
  355. else:
  356. return view[offset : (offset + size)]
  357. def networkString(s: str) -> bytes:
  358. """
  359. Convert a string to L{bytes} using ASCII encoding.
  360. This is useful for sending text-like bytes that are constructed using
  361. string interpolation. For example::
  362. networkString("Hello %d" % (n,))
  363. @param s: A string to convert to bytes.
  364. @type s: L{str}
  365. @raise UnicodeError: The input string is not ASCII encodable.
  366. @raise TypeError: The input is not L{str}.
  367. @rtype: L{bytes}
  368. """
  369. if not isinstance(s, str):
  370. raise TypeError("Can only convert strings to bytes")
  371. return s.encode("ascii")
  372. @deprecated(Version("Twisted", 21, 2, 0), replacement="os.environb")
  373. def bytesEnviron():
  374. """
  375. Return a L{dict} of L{os.environ} where all text-strings are encoded into
  376. L{bytes}.
  377. This function is POSIX only; environment variables are always text strings
  378. on Windows.
  379. """
  380. encodekey = os.environ.encodekey
  381. encodevalue = os.environ.encodevalue
  382. return {encodekey(x): encodevalue(y) for x, y in os.environ.items()} # type: ignore[call-arg]
  383. def _constructMethod(cls, name, self):
  384. """
  385. Construct a bound method.
  386. @param cls: The class that the method should be bound to.
  387. @type cls: L{type}
  388. @param name: The name of the method.
  389. @type name: native L{str}
  390. @param self: The object that the method is bound to.
  391. @type self: any object
  392. @return: a bound method
  393. @rtype: L{_MethodType}
  394. """
  395. func = cls.__dict__[name]
  396. return _MethodType(func, self)
  397. def _get_async_param(isAsync=None, **kwargs):
  398. """
  399. Provide a backwards-compatible way to get async param value that does not
  400. cause a syntax error under Python 3.7.
  401. @param isAsync: isAsync param value (should default to None)
  402. @type isAsync: L{bool}
  403. @param kwargs: keyword arguments of the caller (only async is allowed)
  404. @type kwargs: L{dict}
  405. @raise TypeError: Both isAsync and async specified.
  406. @return: Final isAsync param value
  407. @rtype: L{bool}
  408. """
  409. if "async" in kwargs:
  410. warnings.warn(
  411. "'async' keyword argument is deprecated, please use isAsync",
  412. DeprecationWarning,
  413. stacklevel=2,
  414. )
  415. if isAsync is None and "async" in kwargs:
  416. isAsync = kwargs.pop("async")
  417. if kwargs:
  418. raise TypeError
  419. return bool(isAsync)
  420. def _pypy3BlockingHack():
  421. """
  422. Work around U{https://foss.heptapod.net/pypy/pypy/-/issues/3051}
  423. by replacing C{socket.fromfd} with a more conservative version.
  424. """
  425. try:
  426. from fcntl import F_GETFL, F_SETFL, fcntl
  427. except ImportError:
  428. return
  429. if not _PYPY:
  430. return
  431. def fromFDWithoutModifyingFlags(fd, family, type, proto=None):
  432. passproto = [proto] * (proto is not None)
  433. flags = fcntl(fd, F_GETFL)
  434. try:
  435. return realFromFD(fd, family, type, *passproto)
  436. finally:
  437. fcntl(fd, F_SETFL, flags)
  438. realFromFD = socket.fromfd
  439. if realFromFD.__name__ == fromFDWithoutModifyingFlags.__name__:
  440. return
  441. socket.fromfd = fromFDWithoutModifyingFlags
  442. _pypy3BlockingHack()
  443. deprecatedModuleAttribute(
  444. Version("Twisted", 21, 2, 0),
  445. "Use functools.reduce() directly",
  446. __name__,
  447. "reduce",
  448. )
  449. deprecatedModuleAttribute(
  450. Version("Twisted", 21, 2, 0),
  451. "Use io.StringIO directly",
  452. __name__,
  453. "NativeStringIO",
  454. )
  455. deprecatedModuleAttribute(
  456. Version("Twisted", 21, 2, 0),
  457. "Import urllib.parse directly",
  458. __name__,
  459. "urllib_parse",
  460. )
  461. deprecatedModuleAttribute(
  462. Version("Twisted", 21, 2, 0), "Use html.escape directly", __name__, "escape"
  463. )
  464. deprecatedModuleAttribute(
  465. Version("Twisted", 21, 2, 0),
  466. "Use urllib.parse.quote() directly",
  467. __name__,
  468. "urlquote",
  469. )
  470. deprecatedModuleAttribute(
  471. Version("Twisted", 21, 2, 0),
  472. "Use urllib.parse.unquote() directly",
  473. __name__,
  474. "urlunquote",
  475. )
  476. deprecatedModuleAttribute(
  477. Version("Twisted", 21, 2, 0),
  478. "Use http.cookiejar directly",
  479. __name__,
  480. "cookielib",
  481. )
  482. deprecatedModuleAttribute(
  483. Version("Twisted", 21, 2, 0), "Use sys.intern() directly", __name__, "intern"
  484. )
  485. deprecatedModuleAttribute(
  486. Version("Twisted", 21, 2, 0),
  487. "Use collections.abc.Sequence directly",
  488. __name__,
  489. "Sequence",
  490. )
  491. __all__ = [
  492. "reraise",
  493. "execfile",
  494. "frozenset",
  495. "reduce",
  496. "set",
  497. "cmp",
  498. "comparable",
  499. "nativeString",
  500. "NativeStringIO",
  501. "networkString",
  502. "unicode",
  503. "iterbytes",
  504. "intToBytes",
  505. "lazyByteSlice",
  506. "StringType",
  507. "InstanceType",
  508. "FileType",
  509. "items",
  510. "iteritems",
  511. "itervalues",
  512. "range",
  513. "xrange",
  514. "urllib_parse",
  515. "bytesEnviron",
  516. "escape",
  517. "urlquote",
  518. "urlunquote",
  519. "cookielib",
  520. "intern",
  521. "unichr",
  522. "raw_input",
  523. "_get_async_param",
  524. "Sequence",
  525. ]