From 966ab81a369386eaf0effb59acbe39b5e5537bcb Mon Sep 17 00:00:00 2001 From: Jimmy Lai Date: Sun, 15 Sep 2019 22:28:21 -0700 Subject: [PATCH] add ScopeProvider document --- docs/source/conf.py | 88 ++++++++++++++++--------------- docs/source/metadata.rst | 23 +++++++- libcst/__init__.py | 22 ++++++++ libcst/metadata/scope_provider.py | 48 +++++++++++++++++ 4 files changed, 138 insertions(+), 43 deletions(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index 94df5e89..39338dc4 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -24,14 +24,14 @@ # -- Project information ----------------------------------------------------- -project = 'LibCST' -copyright = '2019, Facebook' -author = 'Benjamin Woodruff, Jennifer Taylor, Carl Meyer, Jimmy Lai, Ray Zeng' +project = "LibCST" +copyright = "2019, Facebook" +author = "Benjamin Woodruff, Jennifer Taylor, Carl Meyer, Jimmy Lai, Ray Zeng" # The short X.Y version -version = '' +version = "" # The full version, including alpha/beta/rc tags -release = '' +release = "" # -- General configuration --------------------------------------------------- @@ -44,25 +44,25 @@ release = '' # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ - 'nbsphinx', - 'sphinx.ext.autodoc', - 'sphinx.ext.autosummary', - 'sphinx.ext.graphviz', - 'sphinx.ext.intersphinx', - 'sphinx_rtd_theme', + "nbsphinx", + "sphinx.ext.autodoc", + "sphinx.ext.autosummary", + "sphinx.ext.graphviz", + "sphinx.ext.intersphinx", + "sphinx_rtd_theme", ] # Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] +templates_path = ["_templates"] # The suffix(es) of source filenames. # You can specify multiple suffix as a list of string: # # source_suffix = ['.rst', '.md'] -source_suffix = '.rst' +source_suffix = ".rst" # The master toctree document. -master_doc = 'index' +master_doc = "index" # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. @@ -74,7 +74,7 @@ language = None # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. # This pattern also affects html_static_path and html_extra_path. -exclude_patterns = ['_build', '**.ipynb_checkpoints'] +exclude_patterns = ["_build", "**.ipynb_checkpoints"] # The name of the Pygments (syntax highlighting) style to use. pygments_style = None @@ -85,7 +85,7 @@ pygments_style = None # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # -html_theme = 'sphinx_rtd_theme' +html_theme = "sphinx_rtd_theme" # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the @@ -96,7 +96,7 @@ html_theme_options = {"logo_only": True} # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] +html_static_path = ["_static"] # Custom sidebar templates, must be a dictionary that maps document names # to template names. @@ -114,7 +114,7 @@ html_favicon = "_static/logo/favicon.ico" # -- Options for HTMLHelp output --------------------------------------------- # Output file base name for HTML help builder. -htmlhelp_basename = 'LibCSTdoc' +htmlhelp_basename = "LibCSTdoc" # -- Options for LaTeX output ------------------------------------------------ @@ -123,15 +123,12 @@ latex_elements = { # The paper size ('letterpaper' or 'a4paper'). # # 'papersize': 'letterpaper', - # The font size ('10pt', '11pt' or '12pt'). # # 'pointsize': '10pt', - # Additional stuff for the LaTeX preamble. # # 'preamble': '', - # Latex figure (float) alignment # # 'figure_align': 'htbp', @@ -141,8 +138,13 @@ latex_elements = { # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ - (master_doc, 'LibCST.tex', 'LibCST Documentation', - 'Benjamin Woodruff, Jennifer Taylor', 'manual'), + ( + master_doc, + "LibCST.tex", + "LibCST Documentation", + "Benjamin Woodruff, Jennifer Taylor", + "manual", + ) ] @@ -150,10 +152,7 @@ latex_documents = [ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). -man_pages = [ - (master_doc, 'libcst', 'LibCST Documentation', - [author], 1) -] +man_pages = [(master_doc, "libcst", "LibCST Documentation", [author], 1)] # -- Options for Texinfo output ---------------------------------------------- @@ -162,9 +161,15 @@ man_pages = [ # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ - (master_doc, 'LibCST', 'LibCST Documentation', - author, 'LibCST', 'One line description of project.', - 'Miscellaneous'), + ( + master_doc, + "LibCST", + "LibCST Documentation", + author, + "LibCST", + "One line description of project.", + "Miscellaneous", + ) ] @@ -183,17 +188,15 @@ epub_title = project # epub_uid = '' # A list of files that should not be packed into the epub file. -epub_exclude_files = ['search.html'] +epub_exclude_files = ["search.html"] # -- Extension configuration ------------------------------------------------- +autoclass_content = "both" autodoc_member_order = "bysource" -autodoc_default_options = { - "members": True, - "undoc-members": True, -} +autodoc_default_options = {"members": True, "undoc-members": True} -intersphinx_mapping = {'python': ('https://docs.python.org/3', None)} +intersphinx_mapping = {"python": ("https://docs.python.org/3", None)} # -- Options for todo extension ---------------------------------------------- @@ -202,19 +205,20 @@ todo_include_todos = True # -- autodoc customization def strip_class_signature(app, what, name, obj, options, signature, return_annotation): - if what == 'class': + if what == "class": return (None, return_annotation) return (signature, return_annotation) def strip_class_signature_docstring(app, what, name, obj, options, lines): - if what == 'class': - cls_name = name.split('.')[-1] + if what == "class": + cls_name = name.split(".")[-1] if lines and lines[0].startswith(cls_name): while lines: del lines[0] + def setup(app): - app.connect('autodoc-process-signature', strip_class_signature) - app.connect('autodoc-process-docstring', strip_class_signature_docstring) - app.add_css_file('custom.css') + app.connect("autodoc-process-signature", strip_class_signature) + app.connect("autodoc-process-docstring", strip_class_signature_docstring) + app.add_css_file("custom.css") diff --git a/docs/source/metadata.rst b/docs/source/metadata.rst index 8aa765b7..8ed48045 100644 --- a/docs/source/metadata.rst +++ b/docs/source/metadata.rst @@ -89,4 +89,25 @@ Expression Context Metadata .. autoclass:: libcst.ExpressionContextProvider :no-undoc-members: -.. autoclass:: libcst.ExpressionContext \ No newline at end of file +.. autoclass:: libcst.ExpressionContext + +Scope Metadata +-------------- +.. autoclass:: libcst.ScopeProvider + +.. autoclass:: libcst.BaseAssignment + :no-undoc-members: + +.. autoclass:: libcst.Assignment +.. autoclass:: libcst.BuiltinAssignmemt +.. autoclass:: libcst.Access + +.. autoclass:: libcst.Scope + :no-undoc-members: + +.. autoclass:: libcst.GlobalScope + :no-undoc-members: + +.. autoclass:: libcst.FunctionScope +.. autoclass:: libcst.ClassScope +.. autoclass:: libcst.ComprehensionScope diff --git a/libcst/__init__.py b/libcst/__init__.py index c3c086d6..36089b3e 100644 --- a/libcst/__init__.py +++ b/libcst/__init__.py @@ -200,6 +200,18 @@ from libcst.metadata.position_provider import ( BasicPositionProvider, SyntacticPositionProvider, ) +from libcst.metadata.scope_provider import ( + Access, + Assignment, + BaseAssignment, + BuiltinAssignmemt, + ClassScope, + ComprehensionScope, + FunctionScope, + GlobalScope, + Scope, + ScopeProvider, +) from libcst.metadata.wrapper import MetadataWrapper @@ -396,4 +408,14 @@ __all__ = [ "MetadataWrapper", "ExpressionContext", "ExpressionContextProvider", + "BaseAssignment", + "Assignment", + "BuiltinAssignmemt", + "Access", + "Scope", + "GlobalScope", + "FunctionScope", + "ClassScope", + "ComprehensionScope", + "ScopeProvider", ] diff --git a/libcst/metadata/scope_provider.py b/libcst/metadata/scope_provider.py index f6d5e2e4..6c634a75 100644 --- a/libcst/metadata/scope_provider.py +++ b/libcst/metadata/scope_provider.py @@ -13,6 +13,7 @@ from dataclasses import dataclass from typing import Dict, Iterator, List, MutableMapping, Optional, Tuple, Type, Union import libcst as cst +from libcst._add_slots import add_slots from libcst.metadata.base_provider import BatchableMetadataProvider from libcst.metadata.expression_context_provider import ( ExpressionContext, @@ -20,14 +21,28 @@ from libcst.metadata.expression_context_provider import ( ) +@add_slots @dataclass(frozen=True) class Access: + """ + Access records an access of an assignment. + """ + + #: The name node of the access. A name is an access when the expression context is + #: :attr:`ExpressionContext.LOAD`. node: cst.Name + + #: The scope of the access. Note that a access could be in a child scope of its assignment. scope: "Scope" class BaseAssignment(abc.ABC): + """Abstract base class of :class:`Assignment` and :class:`BuitinAssignment`.""" + + #: The name of assignment. name: str + + #: The scope associates to assignment. scope: "Scope" __accesses: List[Access] @@ -41,11 +56,16 @@ class BaseAssignment(abc.ABC): @property def accesses(self) -> Tuple[Access, ...]: + """Return all accesses of the assignment.""" # we don't want to publicly expose the mutable version of this return tuple(self.__accesses) class Assignment(BaseAssignment): + """An assignment records the name, CSTNode and its accesses.""" + + #: The node of assignment, it could be a :class:`~libcst.Import`, :class:`ImportFrom`, + #: :class:`~libcst.Name`, :class:`~libcst.FunctionDef`, or :class:`~libcst.ClassDef`. node: cst.CSTNode def __init__(self, name: str, scope: "Scope", node: cst.CSTNode) -> None: @@ -54,11 +74,31 @@ class Assignment(BaseAssignment): class BuiltinAssignmemt(BaseAssignment): + """ + A BuiltinAssignment represents an value provide by Python as a builtin, including + `functions `_, + `constants `_, and + `types `_. + """ + pass class Scope(abc.ABC): + """ + Base class of all scope classes. Scope object stores assignments from imports, + variable assignments, function definition or class definition. + A scope has a parent scope which represents the inheritance relationship. That means + an assignment in parent scope is viewable to the child scope and the child scope may + overwrites the assignment by using the same name. + Use ``name in scope`` to check whether a name is viewable in the scope. + Use ``scope[name]`` to retrieve all viewable assignments in the scope. + """ + + #: Parent scope. Note the parent scope of a GlobalScope is itself. parent: "Scope" + + #: Refers to the GlobalScope. globals: "GlobalScope" _assignments: MutableMapping[str, List[BaseAssignment]] @@ -96,6 +136,10 @@ class Scope(abc.ABC): class GlobalScope(Scope): + """ + GlobalScope is the scope of module. All module level assignments are recorded in GlobalScope. + """ + def __init__(self) -> None: self.globals: Scope = self # must be defined before Scope.__init__ is called super().__init__(parent=self) @@ -144,6 +188,10 @@ class LocalScope(Scope, abc.ABC): class FunctionScope(LocalScope): + """ + When a function is defined, it creates a FunctionScope. + """ + pass