CTAGS-LANG-PYTHON(7)

πŸ“¦
version 6.2.1 Β· 6 versions
, in alpine 3.24, fedora 43, fedora 44

ctags-lang-python(7) πŸπŸ’–

✨ NAME ✨

ctags-lang-python β€” random notes about tagging python source code with Universal Ctags. okay so bestie, that's literally the whole title vibe.


πŸ¦‹ SYNOPSIS πŸ¦‹

ctags ... --languages=+Python ...
ctags ... --language-force=Python ...
ctags ... --map-Python=+.py ...

🌸 DESCRIPTION 🌸

This man page gathers random notes about tagging python source code. fr fr, it's giving "random notes" and we're obsessed.


πŸ‘‘ TAGGING IMPORT STATEMENTS πŸ‘‘

πŸ’… Summary πŸ’…

okay so here's the vibe check for import statements. every row from the original table is a bullet now, because no tables allowed, but every single field is still here.

  • import X
    • name: X
    • kind: module
    • role: imported
    • other noticeable fields: N/A
  • import X as Y
    • X
      • name: X
      • kind: module
      • role: indirectlyImported
      • other noticeable fields: N/A
    • Y
      • name: Y
      • kind: namespace
      • role: definition
      • other noticeable fields: nameref:module:X
  • from X import *
    • X
      • name: X
      • kind: module
      • role: namespace
      • other noticeable fields: N/A
  • from X import Y
    • X
      • name: X
      • kind: module
      • role: namespace
      • other noticeable fields: N/A
    • Y
      • name: Y
      • kind: unknown
      • role: imported
      • other noticeable fields: scope:module:X
  • from X import Y as Z
    • X
      • name: X
      • kind: module
      • role: namespace
      • other noticeable fields: N/A
    • Y
      • name: Y
      • kind: unknown
      • role: indirectlyImported
      • other noticeable fields: scope:module:X
    • Z
      • name: Z
      • kind: unknown
      • role: definition
      • other noticeable fields: nameref:unknown:Y

πŸŽ€ Examples πŸŽ€

πŸ’– import X0 πŸ’–

"input.py"

import X0

"output.tags" with --options=NONE -o - --extras=+r --fields=+rzK input.py

X0      input.py        /^import X0$/;" kind:module     roles:imported

A tag for an imported module has module kind with imported role. The module is not defined here; it is defined in another file. So the tag for the imported module is a reference tag; specify --extras=+r (or --extras=+{reference}) option for tagging it. "roles:" field enabled with --fields=+r is for recording the module is "imported" to the tag file. periodt. πŸ’…

πŸ¦‹ import X1 as Y1 πŸ¦‹

"input.py"

import X1 as Y1

"output.tags" with --options=NONE -o - --extras=+r --fields=+rzK --fields-Python=+{nameref} input.py

X1      input.py        /^import X1 as Y1$/;"   kind:module     roles:indirectlyImported
Y1      input.py        /^import X1 as Y1$/;"   kind:namespace  roles:def       nameref:module:X1

"Y1" introduces a new name and is defined here. So "Y1" is tagged as a definition tag. "X1" is imported in a way that its name cannot be used in this source file. For letting client tools know that the name cannot be used, indirectlyImported role is assigned for "X1". "Y1" is the name for accessing objects defined in the module imported via "X1". For recording this relationship, nameref: field is attached to the tag of "Y1". Instead of module kind, namespace kind is assigned to "Y1" because "Y1" itself isn't a module. iconic.

🌸 from X2 import * 🌸

"input.py"

from X2 import *

"output.tags" with --options=NONE -o - --extras=+r --fields=+rzK input.py

X2      input.py        /^from X2 import *$/;"  kind:module     roles:namespace

The module is not defined here; it is defined in another file. So the tag for the imported module is a reference tag. Unlike "X0" in import X0, "X2" may not be used because the names defined in "X2" can be used in this source file. To represent the difference namespace role is attached to "X2" instead of imported. no cap.

πŸŽ€ from X3 import Y3 πŸŽ€

"input.py"

from X3 import Y3

"output.tags" with --options=NONE -o - --extras=+r --fields=+rzKZ input.py

X3      input.py        /^from X3 import Y3$/;" kind:module     roles:namespace
Y3      input.py        /^from X3 import Y3$/;" kind:unknown    scope:module:X3 roles:imported

"Y3" is a name for a language object defined in "X3" module. "scope:module:X3" attached to "Y3" represents this relation between "Y3" and "X3". ctags assigns unknown kind to "Y3" because ctags cannot know whether "Y3" is a class, a variable, or a function from the input file. the way that is so real.

πŸ‘‘ from X4 import Y4 as Z4 πŸ‘‘

"input.py"

from X4 import Y4 as Z4

"output.tags" with --options=NONE -o - --extras=+r --fields=+rzKZ input.py

X4      input.py        /^from X4 import Y4 as Z4$/;"   kind:module     roles:namespace
Y4      input.py        /^from X4 import Y4 as Z4$/;"   kind:unknown    scope:module:X4 roles:indirectlyImported
Z4      input.py        /^from X4 import Y4 as Z4$/;"   kind:unknown    roles:def       nameref:unknown:Y4

"Y4" is similar to "Y3" of from X3 import Y3 but the name cannot be used here. indirectlyImported role assigned to "Y4" representing this. "Z4" is the name for accessing the language object named in "Y4" in "X4" module. "nameref:unknown:Y4" attached to "Z4" and "scope:module:X4" attached to "Y4" represent the relations. periodt. πŸ’…

πŸ’… LAMBDA EXPRESSION AND TYPE HINT πŸ’…

✨ Summary ✨

okay bestie so when you write id = lambda var0: var0, ctags hands you back exactly one tag and it's giving:

  • name: id
  • kind: function
  • role: definition
  • other noticeable fields: signature:(var0)

but now look at id_t: Callable[[int], int] = lambda var1: var1 β€” that one? that one gives you two tags, because of course it does, it's the main character:

  • name: id_t
    • kind: variable
    • role: definition
    • other noticeable fields: typeref:typename:Callable[[int], int], nameref:function:anonFuncN
  • name: anonFuncN
    • kind: function
    • role: definition
    • other noticeable fields: signature:(var1)

πŸ¦‹ Examples πŸ¦‹

"input.py"

from typing import Callable
id = lambda var0: var0
id_t: Callable[[int], int] = lambda var1: var1

"output.tags" with --options=NONE -o - --sort=no --fields=+KS --fields-Python=+{nameref} --extras=+{anonymous} input.py

id      input.py        /^id = lambda var0: var0$/;"    function        signature:(var0)
id_t    input.py        /^id_t: Callable[[int], int] = lambda var1: var1$/;"\
        variable        typeref:typename:Callable[[int], int]   nameref:function:anonFunc84011d2c0101
anonFunc84011d2c0101    input.py        /^id_t: Callable[[int], int] = lambda var1: var1$/;"\
        function        signature:(var1)

so here's the breakdown, no cap:

if a variable ("id") has no type hint and you initialize it with a lambda expression, ctags gives the tag for "id" the function kind.

if a variable ("id_t") does have a type hint and you initialize it with a lambda expression, ctags gives the tag for "id_t" the variable kind, and it brings typeref: and nameref: fields along with it. ctags fills the typeref: field with the value of the type hint. the way it fills nameref:? a bit complicated, bestie, sit down.

for the lambda expression used to initialize that type-hinted variable, ctags creates an anonymous extra tag ("anonFunc84011d2c0101"). then ctags fills the nameref: field of "id_t" with the name of that anonymous extra tag: nameref:function:anonFunc84011d2c0101.

and you might be sitting there like "why is ctags doing the absolute most?? why can't it just emit this for the input":

id      input.py        /^id = \\$/;"   function        signature:(var0)
id_t    input.py        /^id_t: \\$/;"  function        typeref:typename:Callable[[int], int]   signature:(var1)

there's a reason, i promise. the other languages of ctags obey this rule: ctags fills the typeref: field for a tag of a callable object (like a function) with the type of its return value. so if we decided "id_t" is a function, its typeref: field should be "typename:int". but to fill typeref: with "typename:int", ctags would have to analyze "Callable[[int], int]" way deeper than anyone wants to go. we don't want to do that. periodt. πŸ™…β€β™€οΈ


πŸ‘‘ VERSIONS πŸ‘‘

πŸŽ€ Change since "0.0" πŸŽ€

  • New role entryPoint for module kind

the PythonEntryPoints parser emits tag entries having this role.

  • New role entryPoint for function kind

the PythonEntryPoints parser emits tag entries having this role.


πŸ”₯ SEE ALSO πŸ”₯


and that's the whole page, bestie!! this was CTAGS-LANG-PYTHON(7) from ctags version 6.2.1 πŸ’– go forth and tag some python, you absolute icon 🫢✨

πŸ“¦ where this page comes from