Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: integrating full uvl support and improving actions #124

Merged
merged 3 commits into from
Feb 13, 2025
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
80 changes: 58 additions & 22 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
@@ -1,53 +1,89 @@
name: Python analisys
name: Python analysis

on: [push, pull_request]

jobs:
prospector:
setup:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- name: Set up Python 3.9
uses: actions/setup-python@v2
uses: actions/setup-python@v5
with:
python-version: 3.9
- name: Install dependencies
run: |
python -m pip install --upgrade pip
python -m venv venv
source venv/bin/activate
pip install hug prospector[with_everything]==1.10.3
- name: Analysing the code with prospector
pip install setuptools
pip install .
- name: Cache dependencies
uses: actions/cache@v3
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('setup.py') }}
restore-keys: |
${{ runner.os }}-pip-

ruff:
needs: setup # Wait for setup to complete
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python 3.9
uses: actions/setup-python@v5
with:
python-version: 3.9
- name: Restore dependencies from cache
uses: actions/cache@v3
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('setup.py') }}
- name: Installing Ruff linter
run: |
pip install ruff
- name: Running Ruff linter
run: |
source venv/bin/activate
prospector
ruff check .

mypy:
needs: setup # Wait for setup to complete
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- name: Set up Python 3.9
uses: actions/setup-python@v2
uses: actions/setup-python@v5
with:
python-version: 3.9
- name: Install dependencies
- name: Restore dependencies from cache
uses: actions/cache@v3
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('setup.py') }}
- name: Installing mypy
run: |
python -m pip install --upgrade pip
pip install mypy
pip install mypy
- name: Running static types with mypy
run: |
mypy flamapy
mypy -p flamapy

pytest:
needs: setup # Wait for setup to complete
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- name: Set up Python 3.9
uses: actions/setup-python@v2
uses: actions/setup-python@v5
with:
python-version: 3.9
- name: Install dependencies
- name: Restore dependencies from cache
uses: actions/cache@v3
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('setup.py') }}
- name: Installing pytest
run: |
python -m pip install --upgrade pip
pip freeze
pip install pytest
- name: Running test
- name: Running tests
run: |
python -m pytest
pytest
83 changes: 81 additions & 2 deletions flamapy/core/models/ast.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import string
from typing import Any
from enum import Enum


class ASTOperation(Enum):
# logical operators
REQUIRES = 'REQUIRES'
EXCLUDES = 'EXCLUDES'
AND = 'AND'
Expand Down Expand Up @@ -33,6 +35,35 @@ class ASTOperation(Enum):
CEIL = 'CEIL'


LOGICAL_OPERATORS = [ASTOperation.REQUIRES,
ASTOperation.EXCLUDES,
ASTOperation.AND,
ASTOperation.OR,
ASTOperation.XOR,
ASTOperation.IMPLIES,
ASTOperation.NOT,
ASTOperation.EQUIVALENCE]


ARITHMETIC_OPERATORS = [ASTOperation.ADD,
ASTOperation.SUB,
ASTOperation.MUL,
ASTOperation.DIV,
ASTOperation.EQUALS,
ASTOperation.LOWER,
ASTOperation.GREATER,
ASTOperation.LOWER_EQUALS,
ASTOperation.GREATER_EQUALS,
ASTOperation.NOT_EQUALS]


AGGREGATION_OPERATORS = [ASTOperation.SUM,
ASTOperation.AVG,
ASTOperation.LEN,
ASTOperation.FLOOR,
ASTOperation.CEIL]


class Node:

def __init__(self, data: Any, left: 'Node' = None, right: 'Node' = None): # type: ignore
Expand All @@ -53,7 +84,10 @@ def is_unique_term(self) -> bool:
return not self.is_op() and self.left is None

def is_binary_op(self) -> bool:
return not self.is_unique_term() and not self.is_unary_op()
return not self.is_unique_term() and not self.is_unary_op() and not self.is_aggregate_op()

def is_aggregate_op(self) -> bool:
return self.is_op() and self.data in AGGREGATION_OPERATORS

def __str__(self) -> str:
data = self.data.value if self.is_op() else safename(str(self.data))
Expand Down Expand Up @@ -85,6 +119,9 @@ def pretty_str(self) -> str:
res = f'{data}'
elif self.is_unary_op():
res = f'{data} {left}'
elif self.is_aggregate_op():
if self.right is not None:
res = f'{data}({left}, {right})'
else: # binary operation
res = f'{left} {data} {right}'
return res
Expand Down Expand Up @@ -120,6 +157,36 @@ def get_clauses(self) -> list[list[Any]]:
ast = self.to_cnf()
return get_clauses(ast)

def get_operators(self) -> list[ASTOperation]:
operators = list()
stack = [self.root]
while stack:
node = stack.pop()
if node is not None:
if node.is_op():
operators.append(node.data)
if node.is_unary_op():
stack.append(node.left)
elif node.is_binary_op():
stack.append(node.right)
stack.append(node.left)
return operators

def get_operands(self) -> list[Any]:
operands = list()
stack = [self.root]
while stack:
node = stack.pop()
if node is not None:
if node.is_term():
operands.append(node.data)
elif node.is_unary_op():
stack.append(node.left)
elif node.is_binary_op():
stack.append(node.right)
stack.append(node.left)
return operands

def __str__(self) -> str:
return str(self.root)

Expand Down Expand Up @@ -305,4 +372,16 @@ def get_clause_from_or_node(node: Node) -> list[Any]:


def safename(name: str) -> str:
return f'"{name}"' if ' ' in name else name
if '.' in name:
return '.'.join([safe_simple_name(simple_name) for simple_name in name.split('.')])
return safe_simple_name(name)


def safe_simple_name(name: str) -> str:
if name.startswith("'") and name.endswith("'"):
return name
return f'"{name}"' if any(char not in safecharacters() for char in name) else name


def safecharacters() -> str:
return string.ascii_letters + string.digits + '_'
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ def read_requirements(file):

setuptools.setup(
name="flamapy-fw",
version="2.0.1",
version="2.0.2.dev0",
author="Flamapy",
author_email="[email protected]",
description="Flamapy is a Python-based AAFM framework that takes into consideration previous AAFM tool designs and enables multi-solver and multi-metamodel support for the integration of AAFM tooling on the Python ecosystem.",
Expand Down
Loading