Skip to content

Implement every planned transforms #4

Description

@AlphaKR93

Implement every transforms planned in src/terser/_pipeline/transforms/README.md.

  • [TODO] Separate terser_hints package (use monorepo)
  • [TODO] Create a new example package to test transforms

Flags:

  • REQUIRES_IMPORT_RESOLVE: Name resolving/binding must be performed first.
  • REQUIRES_MODULE_RESOLVE: Module linking must be performed first.
  • INFLUENCES_MANGLING: May affect the operation of name mangling; will performed after mangling.

Transforms

  • Contracts (REQUIRES_IMPORT_RESOLVE)
    Apply contracts with syntax: <function_def> -> <transform>. Similar to JetBrains' Code contract annotations.
    Default contracts:

    • typing.cast(_, value) -> value (e.g., func_ = cast("Callable", func) → func_ = func)
    • typing.assert_never(_) -> None (e.g., assert_never(unreachable) → pass)
    • typing.assert_type(value, _) -> value (e.g., d = assert_type(obj, dict) → d=obj)
    • terser_hints.not_none(value) -> value (e.g., t = not_none(optional_t) → t=optional_t)
  • InlineIIFE (REQUIRES_IMPORT_RESOLVE)
    Inline Immediately-Invoked Function Expressions (@lambda _: _())

    Examples

    Original:

    from terser_hints import constant
    
    @constant
    def some_function():
      foo = 0
      def __inc():
        nonlocal foo; foo += 1
        return foo
      return __inc
    
    class Bar:
      @constant
      @staticmethod
      def some_method():
        bar = 1
        def __mul():
          nonlocal bar; bar *= 16
          return bar
        return __mul

    Minified:

    _some_function__foo=0
    def some_function():
      # Indented intentionally for readability
      global _some_function__foo
      _some_function__foo+=1
      return _some_function__foo
    
    class Bar:
      _some_method__bar=1
      @staticmethod
      def some_method():
        # Indented intentionally for readability
        Bar._some_method__bar<<4
        return Bar._some_method__bar
  • RemoveDummyAssignments (REQUIRES_IMPORT_RESOLVE)
    Remove assignments to oneself (x = x, same binding on both side) or assignments to make the type checker happy (_ = some_method_w_return())

  • RemoveLiteralStatements
    Remove expressions that are just a literal constant.
    CHANGED BEHAVIOR: Docstrings are no longer handled in this transform. (Moved to a separate transform)

  • RemoveDocstrings (REQUIRES_IMPORT_RESOLVE)
    When True, remove docstrings that have not been preserved. The removal of module docstrings is determined by --also-modules.

    • REQUIRES_IMPORT_RESOLVE — due to the mark decorator
  • RemoveAnnotations (REQUIRES_IMPORT_RESOLVE)
    Remove type annotations.
    CHANGED BEHAVIOR:

    • typing.Annotated will not be removed regardless of the option.
    • Quoted annotations (foo: "Foo") are always removed without evaluation.
  • RemoveTypeStatements
    Drop type X = ... (PEP695) type alias statements.

  • RemoveTypingClass (REQUIRES_IMPORT_RESOLVE)
    Remove bare typing.Protocol-based classes (unless decorated with @typing.runtime_checkable) and typing.TypedDict-based classes (converted to dict)

  • RemoveGenerics (REQUIRES_IMPORT_RESOLVE)
    Strip generic [T] type aliases (PEP695) from function/class definitions.

  • RemoveOverloads (REQUIRES_IMPORT_RESOLVE)
    Drop @typing.overload-decorated stub defines. Always True when RemoveTypingDecorators is True.

  • RemoveTypingDecorators (REQUIRES_IMPORT_RESOLVE)
    Strip @typing.override, @typing.final.

  • RemoveExplicitReturnNone
    Convert return None to bare return and drops trailing return/return None from a function body.

  • RemoveExplicitBaseClass
    Remove unnecessary base/meta classes. Currently only removes object from a class's bases.

  • RemoveExceptionBrackets (REQUIRES_IMPORT_RESOLVE)
    Remove empty brackets from built-in exception constructors (raise RuntimeError() → raise RuntimeError)
    CHANGED BEHAVIOR: now transform-based.

  • FoldConstants
    Fold constant by evaluating them.

    • Constant arithmetic/unary operations on literals: UNIT_D = 60 * 60 * 24 → UNIT_D=86400, INT_MAX_VALUE = 2 << 31 - 1 → INT_MAX_VALUE=2147483647
    • bool-identity comparisons: x == True/x is True → x, x == False/x is False → not x (where x is not None)
    • Collection constructors: list() → [], dict() → {}, tuple() → (), ("some", "values") → "some","values", set((1, 2)) → {1,2}
  • RemoveDeadBlocks (runs after FoldConstants)
    Collapse if <literal>: <body> and <body> if <literal> else <other> into just the taken branch.

  • ConvertEarlyExits

    • Invert the condition and/or inline if there's an escape keyword in the else or elif body.
    • Merge if <condition>: return a immediately followed by return b into return a if <condition> else b
  • ConvertDynamicAttributes (REQUIRES_IMPORT_RESOLVE)
    Convert getattr/setattr call into bare access/assignment (getattr(obj, "name") → obj.name, setattr(obj, "name", value) → obj.name=value)
    name should be a constant literal and a valid keyword identifier.
    Ignored when getattr is called with a default value (3-arg form).

  • ConvertToInline
    Convert conditions with a single-statement body into inline conditions. (if cond: func(x) → cond and func(x), if foo: bar(); else: baz() → bar() if foo else baz())

  • ConvertToLambda
    Convert functions with a single-return body into lambdas. (def func(...): return expr → func=lambda ...:expr)

  • CombineImports
    Merges consecutive import statements where possible. from module import * is not handled.

  • OptimizeImports (REQUIRES_IMPORT_RESOLVE)
    Remove imports that are not used or are no longer used by transform.
    Module import optimization can be set to three modes via --optimize-module-imports:

    • always: if there's no __all__ or external reference (always optimize)
    • removed: if no longer used by transforms
    • False: always preserve
  • ConvertPass
    Drop pass statements or convert to smallest expression (0).

  • ConvertPosArgs (INFLUENCES_MANGLING)
    Remove positional-only parameter notation (PEP 570 /).
    Performed last since name mangling uses information about positional-only parameter.

  • RemoveDunderAll (INFLUENCES_MANGLING)
    Drops the top-level __all__ assignment.

Experimental transforms

  • ConvertTypeImports
    • Rewrites from typing_extensions import ... to from typing import ... for a fixed set of symbols long-stable in typing, only when the name is on that list.
    • Rewrites from typing import <collections.abc re-exports> to from collections.abc import ..., same as above.
  • InlineFunctions
    Completely remove the function marked with the @terser_hints.inline decorator, and inserts the body at the referenced location.
  • InlineIntFlags
    Remove the enum.IntFlag declaration and replace it with a constant operation.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions