Stringify the argument (with :class:`str`, not :func:`repr` or :hy:func:`hy.repr`) and convert it to a valid Python identifier according to :ref:`Hy's mangling rules `. :: (hy.mangle 'foo-bar) ; => "foo_bar" (hy.mangle "🦑") ; => "hyx_XsquidX" If the st
(s)
| 7 | |
| 8 | |
| 9 | def mangle(s): |
| 10 | """Stringify the argument (with :class:`str`, not :func:`repr` or |
| 11 | :hy:func:`hy.repr`) and convert it to a valid Python identifier according |
| 12 | to :ref:`Hy's mangling rules <mangling>`. :: |
| 13 | |
| 14 | (hy.mangle 'foo-bar) ; => "foo_bar" |
| 15 | (hy.mangle "🦑") ; => "hyx_XsquidX" |
| 16 | |
| 17 | If the stringified argument is already both legal as a Python identifier |
| 18 | and normalized according to Unicode normalization form KC (NFKC), it will |
| 19 | be returned unchanged. Thus, ``hy.mangle`` is idempotent. :: |
| 20 | |
| 21 | (setv x '♦-->♠) |
| 22 | (= (hy.mangle (hy.mangle x)) (hy.mangle x)) ; => True |
| 23 | |
| 24 | Generally, the stringifed input is expected to be parsable as a symbol. As |
| 25 | a convenience, it can also have the syntax of a :ref:`dotted identifier |
| 26 | <dotted-identifiers>`, and ``hy.mangle`` will mangle the dot-delimited |
| 27 | parts separately. :: |
| 28 | |
| 29 | (hy.mangle "a.c!.d") ; => "a.hyx_cXexclamation_markX.d" |
| 30 | """ |
| 31 | |
| 32 | assert s |
| 33 | s = str(s) |
| 34 | |
| 35 | if "." in s and s.strip("."): |
| 36 | return ".".join(mangle(x) if x else "" for x in s.split(".")) |
| 37 | |
| 38 | # Remove and save leading underscores |
| 39 | s2 = s.lstrip(normalizes_to_underscore) |
| 40 | leading_underscores = "_" * (len(s) - len(s2)) |
| 41 | s = s2 |
| 42 | |
| 43 | # Convert hyphens without introducing a new leading underscore |
| 44 | s = s[0] + s[1:].replace("-", "_") if s else s |
| 45 | |
| 46 | # Convert invalid characters or reserved words |
| 47 | if not (leading_underscores + s).isidentifier(): |
| 48 | # Replace illegal characters with their Unicode character |
| 49 | # names, or hexadecimal if they don't have one. |
| 50 | s = "hyx_" + "".join( |
| 51 | c if c != MANGLE_DELIM and ("S" + c).isidentifier() |
| 52 | # We prepend the "S" because some characters aren't |
| 53 | # allowed at the start of an identifier. |
| 54 | else "{0}{1}{0}".format( |
| 55 | MANGLE_DELIM, |
| 56 | unicodedata.name(c, "").lower().replace("-", "H").replace(" ", "_") |
| 57 | or "U{:x}".format(ord(c)), |
| 58 | ) |
| 59 | for c in s |
| 60 | ) |
| 61 | |
| 62 | # Step 5: Add back leading underscores |
| 63 | s = leading_underscores + s |
| 64 | |
| 65 | # Normalize Unicode per PEP 3131. |
| 66 | s = unicodedata.normalize("NFKC", s) |