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

Pump Version python to 3.12, 3.13. Allow pip install for python 3.12, 3.13 #210

Merged
merged 16 commits into from
Dec 20, 2024
Merged
Show file tree
Hide file tree
Changes from 9 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
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.7", "3.8", "3.9", "3.10", "3.11"]
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
steps:
- uses: actions/checkout@v3
- name: Set up Python ${{ matrix.python-version }}
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ corresponding $\LaTeX$ expression:

1. *Which Python versions are supported?*

Syntaxes on **Pythons 3.7 to 3.11** are officially supported, or will be supported.
Syntaxes on **Pythons 3.9 to 3.13** are officially supported, or will be supported.

2. *Which technique is used?*

Expand Down
16 changes: 8 additions & 8 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ build-backend = "hatchling.build"
name = "latexify-py"
description = "Generates LaTeX math description from Python functions."
readme = "README.md"
requires-python = ">=3.7, <3.12"
requires-python = ">=3.9, <3.14"
license = {text = "Apache Software License 2.0"}
authors = [
{name = "Yusuke Oda", email = "[email protected]"}
Expand All @@ -24,11 +24,11 @@ classifiers = [
"Framework :: IPython",
"Framework :: Jupyter",
"License :: OSI Approved :: Apache Software License",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Topic :: Scientific/Engineering :: Mathematics",
"Topic :: Software Development :: Code Generators",
"Topic :: Text Processing :: Markup :: LaTeX",
Expand All @@ -43,17 +43,17 @@ dynamic = [
[project.optional-dependencies]
dev = [
"build>=0.8",
"black>=22.10",
"flake8>=5.0",
"black>=24.3",
"flake8>=6.0",
"isort>=5.10",
"mypy>=0.991",
"mypy>=1.9",
"notebook>=6.5.1",
"pyproject-flake8>=5.0",
"pyproject-flake8>=6.0",
"pytest>=7.1",
"twine>=4.0",
]
mypy = [
"mypy>=0.991",
"mypy>=1.9",
"pytest>=7.1",
]

Expand Down
1 change: 0 additions & 1 deletion src/latexify/analyzers_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
from latexify import analyzers, ast_utils, exceptions, test_utils


@test_utils.require_at_least(8)
@pytest.mark.parametrize(
"code,start,stop,step,start_int,stop_int,step_int",
[
Expand Down
58 changes: 19 additions & 39 deletions src/latexify/ast_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,24 +56,12 @@ def make_constant(value: Any) -> ast.expr:
Raises:
ValueError: Unsupported value type.
"""
if sys.version_info.minor < 8:
if value is None or value is False or value is True:
return ast.NameConstant(value=value)
if value is ...:
return ast.Ellipsis()
if isinstance(value, (int, float, complex)):
return ast.Num(n=value)
if isinstance(value, str):
return ast.Str(s=value)
if isinstance(value, bytes):
return ast.Bytes(s=value)
else:
if (
value is None
or value is ...
or isinstance(value, (bool, int, float, complex, str, bytes))
):
return ast.Constant(value=value)
if (
value is None
or value is ...
or isinstance(value, (bool, int, float, complex, str, bytes))
):
return ast.Constant(value=value)

raise ValueError(f"Unsupported type to generate Constant: {type(value).__name__}")

Expand All @@ -87,13 +75,7 @@ def is_constant(node: ast.AST) -> bool:
Returns:
True if the node is a constant, False otherwise.
"""
if sys.version_info.minor < 8:
return isinstance(
node,
(ast.Bytes, ast.Constant, ast.Ellipsis, ast.NameConstant, ast.Num, ast.Str),
)
else:
return isinstance(node, ast.Constant)
return isinstance(node, ast.Constant)


def is_str(node: ast.AST) -> bool:
Expand All @@ -120,20 +102,12 @@ def extract_int_or_none(node: ast.expr) -> int | None:
Returns:
Extracted int value, or None if extraction failed.
"""
if sys.version_info.minor < 8:
if (
isinstance(node, ast.Num)
and isinstance(node.n, int)
and not isinstance(node.n, bool)
):
return node.n
else:
if (
isinstance(node, ast.Constant)
and isinstance(node.value, int)
and not isinstance(node.n, bool)
):
return node.value
if (
isinstance(node, ast.Constant)
and isinstance(node.value, int)
and not isinstance(node.value, bool)
):
return node.value

return None

Expand Down Expand Up @@ -173,3 +147,9 @@ def extract_function_name_or_none(node: ast.Call) -> str | None:
return node.func.attr

return None


def ast_function_def(*args, **kwargs) -> ast.FunctionDef:
maycuatroi marked this conversation as resolved.
Show resolved Hide resolved
if sys.version_info.minor < 12:
kwargs.pop("type_params", None)
return ast.FunctionDef(*args, **kwargs)
maycuatroi marked this conversation as resolved.
Show resolved Hide resolved
59 changes: 29 additions & 30 deletions src/latexify/ast_utils_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,19 +34,18 @@ def test_make_attribute() -> None:
)


@test_utils.require_at_most(7)
maycuatroi marked this conversation as resolved.
Show resolved Hide resolved
@pytest.mark.parametrize(
maycuatroi marked this conversation as resolved.
Show resolved Hide resolved
"value,expected",
[
(None, ast.NameConstant(value=None)),
(False, ast.NameConstant(value=False)),
(True, ast.NameConstant(value=True)),
(..., ast.Ellipsis()),
(123, ast.Num(n=123)),
(4.5, ast.Num(n=4.5)),
(6 + 7j, ast.Num(n=6 + 7j)),
("foo", ast.Str(s="foo")),
(b"bar", ast.Bytes(s=b"bar")),
(None, ast.Constant(value=None)),
(False, ast.Constant(value=False)),
(True, ast.Constant(value=True)),
(..., ast.Constant(value=Ellipsis)),
(123, ast.Constant(value=123)),
(4.5, ast.Constant(value=4.5)),
(6 + 7j, ast.Constant(value=6 + 7j)),
("foo", ast.Constant(value="foo")),
(b"bar", ast.Constant(value=b"bar")),
],
)
def test_make_constant_legacy(value: Any, expected: ast.Constant) -> None:
Expand All @@ -56,7 +55,6 @@ def test_make_constant_legacy(value: Any, expected: ast.Constant) -> None:
)


@test_utils.require_at_least(8)
@pytest.mark.parametrize(
"value,expected",
[
Expand All @@ -83,25 +81,23 @@ def test_make_constant_invalid() -> None:
ast_utils.make_constant(object())


@test_utils.require_at_most(7)
@pytest.mark.parametrize(
"value,expected",
[
(ast.Bytes(s=b"foo"), True),
(ast.Constant("bar"), True),
(ast.Ellipsis(), True),
(ast.NameConstant(value=None), True),
(ast.Num(n=123), True),
(ast.Str(s="baz"), True),
(ast.Expr(value=ast.Num(456)), False),
(ast.Constant(value=b"foo"), True),
(ast.Constant(value="bar"), True),
(ast.Constant(value=...), True),
(ast.Constant(value=None), True),
(ast.Constant(value=123), True),
(ast.Constant(value="baz"), True),
(ast.Expr(value=ast.Constant(value=456)), False),
(ast.Global(names=["qux"]), False),
],
)
def test_is_constant_legacy(value: ast.AST, expected: bool) -> None:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks there exists some test cases from that only @test_utils.require_at_most(7) was removed like this. Could you check the changes entirely and remove them?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The decorator (@test_utils.require_at_most(7)) was previously used to indicate that the test case would run on Python versions >= 3.7. So, even when we were on Python 3.9, the test case was still executed. Now that we've upgraded to at least Python 3.9, the test case should still run as intended. I believe it’s best to keep the test case as it is.

Copy link
Collaborator

@odashi odashi Oct 25, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

require_at_most(k) requires versions equal or less than 3.k. the decorated function does nothing (skip everything) otherwise.

if sys.version_info.minor > minor:
return

assert ast_utils.is_constant(value) is expected


@test_utils.require_at_least(8)
@pytest.mark.parametrize(
"value,expected",
[
Expand All @@ -114,25 +110,23 @@ def test_is_constant(value: ast.AST, expected: bool) -> None:
assert ast_utils.is_constant(value) is expected


@test_utils.require_at_most(7)
@pytest.mark.parametrize(
"value,expected",
[
(ast.Bytes(s=b"foo"), False),
(ast.Constant("bar"), True),
(ast.Ellipsis(), False),
(ast.NameConstant(value=None), False),
(ast.Num(n=123), False),
(ast.Str(s="baz"), True),
(ast.Expr(value=ast.Num(456)), False),
(ast.Constant(value=b"foo"), False),
(ast.Constant(value="bar"), True),
(ast.Constant(value=...), False),
(ast.Constant(value=None), False),
(ast.Constant(value=123), False),
(ast.Constant(value="baz"), True),
(ast.Expr(value=ast.Constant(value=456)), False),
(ast.Global(names=["qux"]), False),
],
)
def test_is_str_legacy(value: ast.AST, expected: bool) -> None:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this test case as well

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same with decorator @test_utils.require_at_most(7)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, removing every test function with decoration @test_utils.require_at_most(7) should be fine.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@odashi Yes, I removed all the test function decorated with @test_utils.require_at_most(7)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@odashi please check and approve the Checks :D

assert ast_utils.is_str(value) is expected


@test_utils.require_at_least(8)
@pytest.mark.parametrize(
"value,expected",
[
Expand Down Expand Up @@ -194,6 +188,7 @@ def test_extract_int_invalid() -> None:
ast.Call(
func=ast.Name(id="hypot", ctx=ast.Load()),
args=[],
keywords=[],
),
"hypot",
),
Expand All @@ -205,13 +200,17 @@ def test_extract_int_invalid() -> None:
ctx=ast.Load(),
),
args=[],
keywords=[],
),
"hypot",
),
(
ast.Call(
func=ast.Call(func=ast.Name(id="foo", ctx=ast.Load()), args=[]),
func=ast.Call(
func=ast.Name(id="foo", ctx=ast.Load()), args=[], keywords=[]
),
args=[],
keywords=[],
),
None,
),
Expand Down
4 changes: 1 addition & 3 deletions src/latexify/codegen/expression_codegen_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

import pytest

from latexify import ast_utils, exceptions, test_utils
from latexify import ast_utils, exceptions
from latexify.codegen import expression_codegen


Expand Down Expand Up @@ -792,7 +792,6 @@ def test_visit_boolop(code: str, latex: str) -> None:
assert expression_codegen.ExpressionCodegen().visit(tree) == latex


@test_utils.require_at_most(7)
@pytest.mark.parametrize(
"code,cls,latex",
[
Expand All @@ -817,7 +816,6 @@ def test_visit_constant_lagacy(code: str, cls: type[ast.expr], latex: str) -> No
assert expression_codegen.ExpressionCodegen().visit(tree) == latex


@test_utils.require_at_least(8)
@pytest.mark.parametrize(
"code,latex",
[
Expand Down
42 changes: 35 additions & 7 deletions src/latexify/codegen/expression_rules_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,41 @@
@pytest.mark.parametrize(
"node,precedence",
[
(ast.Call(), expression_rules._CALL_PRECEDENCE),
(ast.BinOp(op=ast.Add()), expression_rules._PRECEDENCES[ast.Add]),
(ast.UnaryOp(op=ast.UAdd()), expression_rules._PRECEDENCES[ast.UAdd]),
(ast.BoolOp(op=ast.And()), expression_rules._PRECEDENCES[ast.And]),
(ast.Compare(ops=[ast.Eq()]), expression_rules._PRECEDENCES[ast.Eq]),
(ast.Name(), expression_rules._INF_PRECEDENCE),
(ast.Attribute(), expression_rules._INF_PRECEDENCE),
(
ast.Call(func=ast.Name(id="func", ctx=ast.Load()), args=[], keywords=[]),
expression_rules._CALL_PRECEDENCE,
),
(
ast.BinOp(
left=ast.Name(id="left", ctx=ast.Load()),
op=ast.Add(),
right=ast.Name(id="right", ctx=ast.Load()),
),
expression_rules._PRECEDENCES[ast.Add],
),
(
ast.UnaryOp(op=ast.UAdd(), operand=ast.Name(id="operand", ctx=ast.Load())),
expression_rules._PRECEDENCES[ast.UAdd],
),
(
ast.BoolOp(op=ast.And(), values=[ast.Name(id="value", ctx=ast.Load())]),
expression_rules._PRECEDENCES[ast.And],
),
(
ast.Compare(
left=ast.Name(id="left", ctx=ast.Load()),
ops=[ast.Eq()],
comparators=[ast.Name(id="right", ctx=ast.Load())],
),
expression_rules._PRECEDENCES[ast.Eq],
),
(ast.Name(id="name", ctx=ast.Load()), expression_rules._INF_PRECEDENCE),
(
ast.Attribute(
value=ast.Name(id="value", ctx=ast.Load()), attr="attr", ctx=ast.Load()
),
expression_rules._INF_PRECEDENCE,
),
],
)
def test_get_precedence(node: ast.AST, precedence: int) -> None:
Expand Down
18 changes: 17 additions & 1 deletion src/latexify/parser_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import pytest

from latexify import exceptions, parser, test_utils
from latexify.ast_utils import ast_function_def
maycuatroi marked this conversation as resolved.
Show resolved Hide resolved


def test_parse_function_with_posonlyargs() -> None:
Expand All @@ -15,14 +16,29 @@ def f(x):

expected = ast.Module(
body=[
ast.FunctionDef(
ast_function_def(
name="f",
args=ast.arguments(
posonlyargs=[],
args=[ast.arg(arg="x")],
vararg=None,
kwonlyargs=[],
kw_defaults=[],
kwarg=None,
defaults=[],
),
body=[ast.Return(value=ast.Name(id="x", ctx=ast.Load()))],
decorator_list=[],
returns=None,
type_comment=None,
type_params=[],
lineno=1,
col_offset=0,
end_lineno=2,
end_col_offset=0,
)
],
type_ignores=[],
)

obtained = parser.parse_function(f)
Expand Down
Loading
Loading