Skip to content
This repository has been archived by the owner on Nov 26, 2023. It is now read-only.

Add a Github Action Linter #57

Merged
merged 7 commits into from
May 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 59 additions & 0 deletions .github/workflows/linters.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
name: Linters

on:
pull_request:
push:
branches:
- main


concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

permissions:
contents: read

jobs:
flake8:
name: flake8
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.10'
- run: python -m pip install flake8
- name: flake8
# Pinned to v2.0.0.
uses: liskin/gh-problem-matcher-wrap@d8afa2cfb66dd3f982b1950429e652bc14d0d7d2
with:
linters: flake8
run: flake8 --max-line-length 89 --ignore=E203,W503 pycg

isort:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.10'
- run: python -m pip install isort
- name: isort
# Pinned to v2.0.0.
uses: liskin/gh-problem-matcher-wrap@d8afa2cfb66dd3f982b1950429e652bc14d0d7d2
with:
linters: isort
run: isort --check --diff pycg

black:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: black
uses: psf/black@stable
8 changes: 3 additions & 5 deletions pycg/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ def main():
type=int,
help=(
"Maximum number of iterations through source code. "
+ "If not specified a fix-point iteration will be performed."
"If not specified a fix-point iteration will be performed."
),
default=-1,
)
Expand All @@ -40,11 +40,9 @@ def main():
type=str,
choices=[CALL_GRAPH_OP, KEY_ERR_OP],
help=(
"Operation to perform. "
+ "Choose "
"Operation to perform. Choose "
+ CALL_GRAPH_OP
+ " for call graph generation (default)"
+ " or "
+ " for call graph generation (default) or "
+ KEY_ERR_OP
+ " for key error detection on dictionaries."
),
Expand Down
6 changes: 3 additions & 3 deletions pycg/formats/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,6 @@
# specific language governing permissions and limitations
# under the License.
#
from .fasten import Fasten
from .simple import Simple
from .as_graph import AsGraph
from .as_graph import AsGraph # noqa: F401
from .fasten import Fasten # noqa: F401
from .simple import Simple # noqa: F401
gdrosos marked this conversation as resolved.
Show resolved Hide resolved
5 changes: 3 additions & 2 deletions pycg/formats/fasten.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ def to_uri(self, modname, name=""):
if not name.startswith(modname + "."):
raise Exception("name should start with modname", name, modname)

cleared = name[len(modname) + 1 :]
cleared = name[(len(modname) + 1) :]

suffix = ""
if name in self.functions:
Expand Down Expand Up @@ -91,7 +91,8 @@ def find_dependencies(self, package_path):
try:
req = Requirement.parse(line)
except ValueError:
# The specific line in the requirements.txt does not follow the Requirements File Format
# The specific line in the requirements.txt
# does not follow the Requirements File Format
continue

specs = req.specs
Expand Down
6 changes: 4 additions & 2 deletions pycg/machinery/definitions.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,8 @@ def update_pointsto_args(pointsto_args, arg, name):
# if we remove this the following breaks:
# x = lambda x: x + 1
# x(1)
# since on line 184 we don't discriminate between literal values and name values
# since on line 184 we don't discriminate between
# literal values and name values
if not self.defs.get(item, None):
continue
pointsto_arg_def.add(item)
Expand All @@ -160,7 +161,8 @@ def update_pointsto_args(pointsto_args, arg, name):
continue

pointsto_name_pointer = self.defs[name].get_name_pointer()
# iterate the arguments of the definition we're currently iterating
# iterate the arguments of the definition
# we're currently iterating
for arg_name, arg in current_name_pointer.get_args().items():
pos = current_name_pointer.get_pos_of_name(arg_name)
if pos is not None:
Expand Down
6 changes: 4 additions & 2 deletions pycg/machinery/imports.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,8 +135,10 @@ def _handle_import_level(self, name, level):
raise ImportError("Attempting import beyond top level package")

mod_name = ("." * level) + name
# When an __init__ file is analyzed, then the module name doesn't contain
# the __init__ part in it, so special care must be taken for levels.
# When an __init__ file is analyzed,
# then the module name doesn't contain
# the __init__ part in it,
# so special care must be taken for levels.
if self._is_init_file() and level >= 1:
if level != 1:
level -= 1
Expand Down
20 changes: 12 additions & 8 deletions pycg/processing/cgprocessor.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,8 @@ def create_ext_edge(name, ext_modname):
names = self.retrieve_call_names(node)
if not names:
if isinstance(node.func, ast.Attribute) and self.has_ext_parent(node.func):
# TODO: This doesn't work for cases where there is an assignment of an attribute
# TODO: This doesn't work for cases
# where there is an assignment of an attribute
# i.e. import os; lala = os.path; lala.dirname()
for name in self.get_full_attr_names(node.func):
ext_modname = name.split(".")[0]
Expand All @@ -165,13 +166,16 @@ def create_ext_edge(name, ext_modname):
continue
self.call_graph.add_edge(self.current_method, pointer)

# TODO: This doesn't work and leads to calls from the decorators
# themselves to the function, creating edges to the first decorator
# for decorator in pointer_def.decorator_names:
# dec_names = self.closured.get(decorator, [])
# for dec_name in dec_names:
# if self.def_manager.get(dec_name).get_type() == utils.constants.FUN_DEF:
# self.call_graph.add_edge(self.current_ns, dec_name)
# TODO: This doesn't work
# and leads to calls from the decorators
# themselves to the function,
# creating edges to the first decorator
# for decorator in pointer_def.decorator_names:
# dec_names = self.closured.get(decorator, [])
# for dec_name in dec_names:
# if self.def_manager.get(dec_name).
# get_type() == utils.constants.FUN_DEF:
# self.call_graph.add_edge(self.current_ns, dec_name)

if pointer_def.get_type() == utils.constants.CLS_DEF:
init_ns = self.find_cls_fun_ns(pointer, utils.constants.CLS_INIT)
Expand Down
6 changes: 4 additions & 2 deletions pycg/processing/postprocessor.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,8 @@ def visit_For(self, node):
if next_defi:
for name in self.closured.get(next_defi.get_ns(), []):
target_def.get_name_pointer().add(name)
else: # otherwise, add a pointer to the name (e.g. a yield)
else: # otherwise, add a pointer to the name
# (e.g. a yield)
target_def.get_name_pointer().add(name)

super().visit_For(node)
Expand Down Expand Up @@ -144,7 +145,8 @@ def visit_FunctionDef(self, node):

previous_names = self.closured.get(fn_def.get_ns(), set())
for decorator in reversed_decorators:
# assign the previous_def as the first parameter of the decorator
# assign the previous_def
# as the first parameter of the decorator
decoded = self.decode_node(decorator)
new_previous_names = set()
for d in decoded:
Expand Down
3 changes: 2 additions & 1 deletion pycg/processing/preprocessor.py
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,8 @@ def _handle_function_def(self, node, fn_name):
defs_to_create = []
name_pointer = fn_def.get_name_pointer()

# TODO: static methods can be created using the staticmethod() function too
# TODO: static methods can be created using
# the staticmethod() function too
is_static_method = False
if hasattr(node, "decorator_list"):
for decorator in node.decorator_list:
Expand Down
4 changes: 2 additions & 2 deletions pycg/tests/fasten_format_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ def test_uri(self):
self.cg_generator.functions = ["mod1.mod2.myfunc"]
formatter = self.get_formatter()

### Internal uri check
# Internal uri check
# test modname without method
self.assertEqual(formatter.to_uri("mymod"), "/mymod/")
self.assertEqual(formatter.to_uri("mymod.mod1"), "/mymod.mod1/")
Expand All @@ -109,7 +109,7 @@ def test_uri(self):
formatter.to_uri("mod1.mod2", "mod1.mod2.myfunc"), "/mod1.mod2/myfunc()"
)

### External uri check
# External uri check
# test modname builtin
self.assertEqual(
formatter.to_external_uri(
Expand Down
2 changes: 1 addition & 1 deletion pycg/utils/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,5 @@
# specific language governing permissions and limitations
# under the License.
#
from . import constants
from . import constants # noqa: F401
from .common import * # noqa
31 changes: 17 additions & 14 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,36 +23,39 @@
from setuptools import setup, find_packages
from subprocess import call


def get_long_desc():
with open("README.md", "r") as readme:
desc = readme.read()

return desc


def setup_package():
setup(
name='pycg',
version='0.0.6',
description='Practical Python Call Graphs',
name="pycg",
version="0.0.6",
description="Practical Python Call Graphs",
long_description=get_long_desc(),
long_description_content_type="text/markdown",
url='https://github.com/vitsalis/pycg',
license='Apache Software License',
url="https://github.com/vitsalis/pycg",
license="Apache Software License",
packages=find_packages(),
install_requires=[],
python_requires='>=3.4',
entry_points = {
'console_scripts': [
'pycg=pycg.__main__:main',
python_requires=">=3.4",
entry_points={
"console_scripts": [
"pycg=pycg.__main__:main",
],
},
classifiers=[
'License :: OSI Approved :: Apache Software License',
'Programming Language :: Python :: 3'
"License :: OSI Approved :: Apache Software License",
"Programming Language :: Python :: 3",
],
author = 'Vitalis Salis',
author_email = 'vitsalis@gmail.com'
author="Vitalis Salis",
author_email="vitsalis@gmail.com",
)

if __name__ == '__main__':

if __name__ == "__main__":
setup_package()