Skip to content

Commit ea109c5

Browse files
committed
Add back syntax highlighting for builtins and imported modules
it's more selective than before tho
1 parent e0f0074 commit ea109c5

File tree

1 file changed

+61
-21
lines changed

1 file changed

+61
-21
lines changed

sphinx_github_style/meth_lexer.py

Lines changed: 61 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,80 @@
1-
import types
1+
import builtins
22
from pathlib import Path
3-
from typing import Type, Optional
3+
from types import ModuleType
4+
from typing import Type, Optional, Set
45
from pygments.lexers.python import NumPyLexer
5-
from inspect import getmembers, isfunction, ismethod, ismodule, isclass
6+
from inspect import getmembers, isfunction, ismethod, ismodule, isclass, isbuiltin, ismethoddescriptor
67

78

8-
def is_module_in_package(object):
9-
"""Predicate to determine if an object is a module within the project's top level package"""
10-
return ismodule(object) and object.__name__.startswith(TDKMethLexer.TOP_LEVEL)
9+
def is_module_in_package(object, top_level):
10+
"""Predicate to determine if an object is a module within a top level package"""
11+
return ismodule(object) and object.__name__.startswith(top_level)
1112

1213

13-
def is_class_in_package(object):
14-
"""Predicate to determine if an object is a class within the project's top level package"""
15-
return isclass(object) and object.__module__.startswith(TDKMethLexer.TOP_LEVEL)
14+
def is_class_in_package(object, top_level):
15+
"""Predicate to determine if an object is a class within a top level package"""
16+
return isclass(object) and object.__module__.startswith(top_level)
1617

1718

18-
def get_pkg_funcs(pkg_module: types.ModuleType, funcs_meths=set(), processed_modules=set()):
19+
def get_pkg_funcs(pkg_module: ModuleType, top_level: str, funcs_meths: Set[str] = set(), processed_modules: Set[ModuleType] = set()) -> Set[str]:
20+
"""Finds all functions and methods that are defined within a package and its subpackages.
21+
22+
For external modules that are imported into the package, only the functions and methods
23+
that are directly within the module are included.
24+
25+
:param pkg_module: the package's top-level module
26+
:param top_level: the name of the top-level module
27+
:param funcs_meths: a set of function/method names
28+
:param processed_modules: a set of already processed modules
29+
:return: a set containing the names of all found functions and methods
30+
"""
1931
funcs_meths.update(get_funcs(pkg_module))
2032
processed_modules.add(pkg_module)
2133

22-
for class_name, _class in getmembers(pkg_module, is_class_in_package):
34+
for class_name, _class in getmembers(pkg_module, isclass):
2335
funcs_meths.update(get_funcs(_class))
2436

25-
for mod_name, mod in getmembers(pkg_module, is_module_in_package):
37+
for mod_name, mod in getmembers(pkg_module, ismodule):
2638
if mod in processed_modules:
2739
continue
2840

41+
processed_modules.add(mod)
42+
2943
try:
3044
mod_path = Path(mod.__file__)
31-
except AttributeError:
32-
continue # It's a built-in module
45+
except AttributeError: # It's a built-in module
46+
funcs_meths.update(get_funcs(mod))
47+
continue
3348

34-
if mod_path.name == '__init__.py': # If it's a subpackage, call recursively to process all submodules
35-
get_pkg_funcs(mod, funcs_meths, processed_modules)
49+
if is_module_in_package(mod, top_level):
50+
if mod_path.name == '__init__.py': # If it's a subpackage, call recursively on submodules
51+
get_pkg_funcs(mod, top_level, funcs_meths, processed_modules)
52+
else:
53+
funcs_meths.update(get_funcs(mod))
54+
55+
else: # For external modules, avoid recursion into submodules
56+
get_funcs_from_external_module(mod, funcs_meths)
57+
58+
return funcs_meths
3659

37-
else: # If it's not a subpackage, get all funcs/meths defined in it
38-
funcs_meths.update(get_funcs(mod))
39-
processed_modules.add(mod)
60+
61+
def get_funcs_from_external_module(module: ModuleType, funcs_meths: Set[str]):
62+
"""Adds functions/methods contained within an imported external module"""
63+
funcs_meths.update(get_funcs(module))
64+
top_level = module.__name__
65+
66+
for class_name, _class in getmembers(module, lambda obj: is_class_in_package(obj, top_level)):
67+
funcs_meths.update(get_funcs(_class))
68+
69+
return funcs_meths
70+
71+
72+
def get_builtin_funcs():
73+
funcs_meths = set(dict(getmembers(builtins, isbuiltin)))
74+
75+
for class_name, _class in getmembers(builtins, isclass):
76+
methods = getmembers(_class, ismethoddescriptor)
77+
funcs_meths.update(set(dict(methods)))
4078

4179
return funcs_meths
4280

@@ -53,7 +91,9 @@ class TDKMethLexer(NumPyLexer):
5391
name = 'TDK'
5492
url = 'https://github.com/TDKorn'
5593
aliases = ['tdk']
94+
5695
TOP_LEVEL = None
96+
EXTRA_KEYWORDS = get_builtin_funcs()
5797

5898
@classmethod
5999
def get_pkg_lexer(cls, pkg_name: Optional[str] = None) -> Type["TDKMethLexer"]:
@@ -64,8 +104,8 @@ def get_pkg_lexer(cls, pkg_name: Optional[str] = None) -> Type["TDKMethLexer"]:
64104
raise ValueError('Must provide a package name')
65105

66106
pkg = __import__(cls.TOP_LEVEL)
67-
funcs = get_pkg_funcs(pkg)
68-
cls.EXTRA_KEYWORDS = funcs
107+
funcs = get_pkg_funcs(pkg, cls.TOP_LEVEL)
108+
cls.EXTRA_KEYWORDS.update(funcs)
69109
return cls
70110

71111

0 commit comments

Comments
 (0)