Skip to content

Commit

Permalink
fix class private check #79
Browse files Browse the repository at this point in the history
  • Loading branch information
StardustDL committed Apr 1, 2024
1 parent 9953a93 commit 4943816
Show file tree
Hide file tree
Showing 15 changed files with 538 additions and 486 deletions.
6 changes: 3 additions & 3 deletions docs/change-spec/description.md
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ Change type of return value for a function.

$$e,e'\in F\wedge \mathbf{return}(e)\ne \bot\wedge\mathbf{return}(e')\ne \bot\wedge\mathbf{return}(e)\ne\mathbf{return}(e')$$

- 🟠 Medium **if** $\mathbf{return}(e')\subseteq\mathbf{return}(e)$
- 🟠 Medium **if** $\mathbf{return}(e')\not\subseteq\mathbf{return}(e)$

# Attribute (5)

Expand Down Expand Up @@ -199,7 +199,7 @@ Change type of an attribute.

$$e,e'\in A\wedge\mathbf{type}(e)\ne \bot\wedge\mathbf{type}(e')\ne \bot\wedge\mathbf{type}(e)\ne\mathbf{type}(e')$$

- 🟠 Medium **if** $\mathbf{type}(e')\subseteq\mathbf{type}(e)$
- 🟠 Medium **if** $\mathbf{type}(e')\not\subseteq\mathbf{type}(e)$

# Parameter (17)

Expand Down Expand Up @@ -355,7 +355,7 @@ Change type of a parameter.

$$\mathbf{type}(p)\ne \bot\wedge\mathbf{type}(p')\ne \text{None}\wedge\mathbf{type}(p)\ne\mathbf{type}(p')$$

- 🟠 Medium **if** $\mathbf{type}(p)\subseteq\mathbf{type}(p')$
- 🟠 Medium **if** $\mathbf{type}(p)\not\subseteq\mathbf{type}(p')$

# Alias (6)

Expand Down
8 changes: 8 additions & 0 deletions scripts/run.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import subprocess
import sys
from pathlib import Path

if __name__ == "__main__":
subprocess.run(
[sys.executable, "-m", "aexpy"] + sys.argv[1:], cwd=Path(__file__).parent.parent / "src"
)
13 changes: 4 additions & 9 deletions src/aexpy/apidetector/processor.py
Original file line number Diff line number Diff line change
Expand Up @@ -196,8 +196,8 @@ def visitModule(self, /, obj, parent: str = ""):
self._visitEntry(res, obj)
self.addEntry(res)

publicMembers = getattr(obj, "__all__", None)
res.slots = {str(s) for s in (publicMembers or [])}
if hasattr(obj, "__all__"):
res.slots = {str(s) for s in getattr(obj, "__all__")}

for mname, member in inspect.getmembers(obj):
entry = None
Expand All @@ -222,12 +222,6 @@ def visitModule(self, /, obj, parent: str = ""):
)
if not entry.annotation:
entry.annotation = res.annotations.get(mname) or ""
if (
publicMembers is not None
and isinstance(entry, ApiEntry)
and entry.parent == res.id
):
entry.private = mname not in publicMembers
except Exception:
self.logger.error(
f"Failed to extract module member {id}.{mname}: {member}",
Expand Down Expand Up @@ -266,13 +260,14 @@ def visitClass(self, /, obj, parent: str = ""):
bases=[self.getObjectId(b) for b in bases],
abcs=abcs,
mros=[self.getObjectId(b) for b in inspect.getmro(obj)],
slots={str(s) for s in getattr(obj, "__slots__", [])},
parent=id.rsplit(".", 1)[0] if "." in id else parent,
flags=(ClassFlag.Abstract if inspect.isabstract(obj) else ClassFlag.Empty)
| (ClassFlag.Final if isFinal(obj) else ClassFlag.Empty)
| (ClassFlag.Generic if isGeneric(obj) else ClassFlag.Empty)
| (ClassFlag.Dataclass if is_dataclass(obj) else ClassFlag.Empty),
)
if hasattr(obj, "__slots__"):
res.slots = {str(s) for s in getattr(obj, "__slots__")}
self._visitEntry(res, obj)
self.addEntry(res)

Expand Down
43 changes: 8 additions & 35 deletions src/aexpy/extracting/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,40 +13,6 @@
from .environment import EnvirontmentExtractor


def resolveAlias(api: ApiDescription):
alias: dict[str, set[str]] = {}
working: set[str] = set()

def resolve(entry: ApiEntryType):
if entry.id in alias:
return alias[entry.id]
ret: set[str] = set()
ret.add(entry.id)
working.add(entry.id)
for item in api:
if not isinstance(item, CollectionEntry):
continue
itemalias = None
# ignore submodules and subclasses
if item.id.startswith(f"{entry.id}."):
continue
for name, target in item.members.items():
if target == entry.id:
if itemalias is None:
if item.id in working: # cycle reference
itemalias = {item.id}
else:
itemalias = resolve(item)
for aliasname in itemalias:
ret.add(f"{aliasname}.{name}")
alias[entry.id] = ret
working.remove(entry.id)
return ret

for entry in api:
entry.alias = list(resolve(entry) - {entry.id})


class BaseExtractor(EnvirontmentExtractor):
"""Basic extractor that uses dynamic inspect."""

Expand Down Expand Up @@ -80,9 +46,16 @@ def extractInEnv(self, /, result, runner):
if entry.id not in result:
result.add(entry)

resolveAlias(result)
result.calcAliases()
for item in result:
if isPrivate(item):
item.private = True
for mod in result.modules.values():
if mod.slots is None:
continue
for member, target in mod.members.items():
entry = result[target]
if entry is not None and entry.parent == mod.id and len(entry.alias) == 0:
entry.private = member not in mod.slots

result.calcSubclasses()
33 changes: 33 additions & 0 deletions src/aexpy/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,39 @@ def calcSubclasses(self, /):
entry = self[base]
if isinstance(entry, ClassEntry):
entry.subclasses = list(subclass)

def calcAliases(self):
alias: dict[str, set[str]] = {}
working: set[str] = set()

def resolve(entry: ApiEntryType):
if entry.id in alias:
return alias[entry.id]
ret: set[str] = set()
ret.add(entry.id)
working.add(entry.id)
for item in self:
if not isinstance(item, CollectionEntry):
continue
itemalias = None
# ignore submodules and subclasses
if item.id.startswith(f"{entry.id}."):
continue
for name, target in item.members.items():
if target == entry.id:
if itemalias is None:
if item.id in working: # cycle reference
itemalias = {item.id}
else:
itemalias = resolve(item)
for aliasname in itemalias:
ret.add(f"{aliasname}.{name}")
alias[entry.id] = ret
working.remove(entry.id)
return ret

for entry in self:
entry.alias = list(resolve(entry) - {entry.id})

def name(self, /, name: str):
return (item for item in self if item.name == name)
Expand Down
2 changes: 1 addition & 1 deletion src/aexpy/models/description.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ class ApiEntry(BaseModel):

class CollectionEntry(ApiEntry):
members: dict[str, str] = {}
slots: set[str] = set()
slots: set[str] | None = None
annotations: dict[str, str] = {}

@cached_property
Expand Down
Loading

0 comments on commit 4943816

Please sign in to comment.