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

Add typing to chart and calculus method #35188

Open
wants to merge 8 commits into
base: develop
Choose a base branch
from

Conversation

tobiasdiez
Copy link
Contributor

📚 Description

Adds (almost complete) typing information for charts (and the related calculus method).

📝 Checklist

  • I have made sure that the title is self-explanatory and the description concisely explains the PR.
  • I have linked an issue or discussion.
  • I have created tests covering the changes.
  • I have updated the documentation accordingly.

⌛ Dependencies

@codecov-commenter
Copy link

codecov-commenter commented Feb 24, 2023

Codecov Report

Base: 88.59% // Head: 88.58% // Decreases project coverage by -0.02% ⚠️

Coverage data is based on head (820440f) compared to base (05329f6).
Patch coverage: 91.73% of modified lines in pull request are covered.

Additional details and impacted files
@@             Coverage Diff             @@
##           develop   #35188      +/-   ##
===========================================
- Coverage    88.59%   88.58%   -0.02%     
===========================================
  Files         2140     2140              
  Lines       396998   397304     +306     
===========================================
+ Hits        351740   351943     +203     
- Misses       45258    45361     +103     
Impacted Files Coverage Δ
src/sage/manifolds/manifold.py 85.38% <87.50%> (ø)
src/sage/manifolds/subset.py 94.00% <88.88%> (-0.19%) ⬇️
src/sage/manifolds/point.py 92.27% <90.00%> (-0.32%) ⬇️
src/sage/manifolds/chart.py 90.48% <91.54%> (-0.51%) ⬇️
src/sage/manifolds/calculus_method.py 97.46% <95.65%> (-1.15%) ⬇️
src/sage/groups/affine_gps/euclidean_group.py 88.88% <0.00%> (-11.12%) ⬇️
...e/combinat/cluster_algebra_quiver/mutation_type.py 50.35% <0.00%> (-4.28%) ⬇️
src/sage/categories/super_modules_with_basis.py 96.29% <0.00%> (-3.71%) ⬇️
src/sage/plot/histogram.py 96.77% <0.00%> (-3.23%) ⬇️
src/sage/groups/affine_gps/affine_group.py 96.62% <0.00%> (-2.25%) ⬇️
... and 47 more

Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here.

☔ View full report at Codecov.
📢 Do you have feedback about the report comment? Let us know in this issue.

@tobiasdiez tobiasdiez mentioned this pull request Mar 31, 2023
5 tasks
vbraun pushed a commit to vbraun/sage that referenced this pull request Aug 23, 2023
    
<!-- Please provide a concise, informative and self-explanatory title.
-->
<!-- Don't put issue numbers in the title. Put it in the Description
below. -->
<!-- For example, instead of "Fixes sagemath#12345", use "Add a new method to
multiply two integers" -->

### 📚 Description

In preparation for important package upgrades, we remove support for
Python v3.8. (Almost) all occurrences of "Python 3.8" are removed from
the codebase. There are still some comments left that essentially say
that in newer versions things can be simplified, but I didn't touch
those and leave them for follow-up PRs.

This will help with:
-  PRs that require Python 3.9 or above, such as
sagemath#34973 and
sagemath#35188.
- Upgrades of Python packages that have dropped support for Python 3.8
because they follow NEP 29 and have already released a new major version
that drops support according to the NEP 29 schedule.
  - NetworkX 3.2 (expected 2023-??; version 3.1 was released 2023-04,
see sagemath#35671)
  - sagemath#34816
  - sagemath#35703
  - See also sagemath#32074.

Test run: https://github.com/tobiasdiez/sage/actions/runs/4586981626

<!-- Describe your changes here in detail. -->
<!-- Why is this change required? What problem does it solve? -->
<!-- If this PR resolves an open issue, please link to it here. For
example "Fixes sagemath#12345". -->
<!-- If your change requires a documentation PR, please link it
appropriately. -->

### 📝 Checklist

<!-- Put an `x` in all the boxes that apply. It should be `[x]` not `[x
]`. -->

- [x] The title is concise, informative, and self-explanatory.
- [x] The description explains in detail what this PR is about.
- [ ] I have linked a relevant issue or discussion.
- [ ] I have created tests covering the changes.
- [x] I have updated the documentation accordingly.

### ⌛ Dependencies

<!-- List all open PRs that this PR logically depends on
- sagemath#12345: short description why this is a dependency
- sagemath#34567: ...
-->

<!-- If you're unsure about any of these, don't hesitate to ask. We're
here to help! -->
    
URL: sagemath#35404
Reported by: Tobias Diez
Reviewer(s): Dima Pasechnik, Matthias Köppe
vbraun pushed a commit to vbraun/sage that referenced this pull request Aug 27, 2023
    
<!-- Please provide a concise, informative and self-explanatory title.
-->
<!-- Don't put issue numbers in the title. Put it in the Description
below. -->
<!-- For example, instead of "Fixes sagemath#12345", use "Add a new method to
multiply two integers" -->

### 📚 Description

In preparation for important package upgrades, we remove support for
Python v3.8. (Almost) all occurrences of "Python 3.8" are removed from
the codebase. There are still some comments left that essentially say
that in newer versions things can be simplified, but I didn't touch
those and leave them for follow-up PRs.

This will help with:
-  PRs that require Python 3.9 or above, such as
sagemath#34973 and
sagemath#35188.
- Upgrades of Python packages that have dropped support for Python 3.8
because they follow NEP 29 and have already released a new major version
that drops support according to the NEP 29 schedule.
  - NetworkX 3.2 (expected 2023-??; version 3.1 was released 2023-04,
see sagemath#35671)
  - sagemath#34816
  - sagemath#35703
  - See also sagemath#32074.

Test run: https://github.com/tobiasdiez/sage/actions/runs/4586981626

<!-- Describe your changes here in detail. -->
<!-- Why is this change required? What problem does it solve? -->
<!-- If this PR resolves an open issue, please link to it here. For
example "Fixes sagemath#12345". -->
<!-- If your change requires a documentation PR, please link it
appropriately. -->

### 📝 Checklist

<!-- Put an `x` in all the boxes that apply. It should be `[x]` not `[x
]`. -->

- [x] The title is concise, informative, and self-explanatory.
- [x] The description explains in detail what this PR is about.
- [ ] I have linked a relevant issue or discussion.
- [ ] I have created tests covering the changes.
- [x] I have updated the documentation accordingly.

### ⌛ Dependencies

<!-- List all open PRs that this PR logically depends on
- sagemath#12345: short description why this is a dependency
- sagemath#34567: ...
-->

<!-- If you're unsure about any of these, don't hesitate to ask. We're
here to help! -->
    
URL: sagemath#35404
Reported by: Tobias Diez
Reviewer(s): Dima Pasechnik, Matthias Köppe
@github-actions
Copy link

Documentation preview for this PR (built with commit c930dca; changes) is ready! 🎉

@tobiasdiez
Copy link
Contributor Author

Now that Python 3.8 is dropped, this PR is finally good to go. @egourgoulhon could you please review it? Thanks!

(The two failing tests are happening on all PRs right now)


# Conversion functions
def _SR_to_Sympy(expression):
def _SR_to_Sympy(expression: Expression) -> Expression:
Copy link
Contributor

Choose a reason for hiding this comment

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

The declared return type is wrong. It returns a sympy object, not an Expression.

And also the declared input is wrong because this function first marshals the input into SR. It is allowed to be all kinds of things.

def is_trivial_zero(self, expression, method=None):
def is_trivial_zero(
self, expression: Expression, method: Optional[CalculusMethodName] = None
) -> bool:
Copy link
Contributor

Choose a reason for hiding this comment

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

Also here, the type declaration Expression is wrong. It depends on method what type is allowed.

def __classcall__(
cls: Type[Chart],
domain: TopologicalManifold,
coordinates: str = "",
Copy link
Contributor

Choose a reason for hiding this comment

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

No, str is only one of the allowed types, see function body

@@ -3416,7 +3520,7 @@ def _latex_(self):
"""
return latex(self._chart1) + r' \rightarrow ' + latex(self._chart2)

def __eq__(self, other):
def __eq__(self, other: CoordChange) -> bool:
Copy link
Contributor

Choose a reason for hiding this comment

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

Not correct. It is allowed to pass an object of a different type as other. The method returns False in this case.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

There seems to be two conventions for this. Either you use indeed object for other, or you restrict other to some meaningful subset. The later is employed by e.g numpy and many others. The logic here seems to be that its more helpful to have your static checker complain that you compare apples and bananas, then to correctly handle the relatively rare case of comparing two completely different object (in which case you can ignore the error).

@@ -3848,7 +3954,7 @@ def restrict(self, dom1, dom2=None):
return type(self)(self._chart1.restrict(dom1),
self._chart2.restrict(dom2), *(self._transf.expr()))

def display(self):
def display(self) -> str:
Copy link
Contributor

Choose a reason for hiding this comment

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

not correct; a FormattedExpansion is not an str.

@@ -1320,7 +1326,7 @@ def get_subset(self, name):

#### End of accessors

def is_subset(self, other):
def is_subset(self, other: TopologicalManifold):
Copy link
Contributor

Choose a reason for hiding this comment

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

Not correct. other is allowed to be a mere ManifoldSubset

@@ -553,7 +602,7 @@ def _latex_(self):
description += latex(self._xx[n-1]).strip() + r')\right)'
return description

def _first_ngens(self, n):
def _first_ngens(self, n: int):
Copy link
Contributor

Choose a reason for hiding this comment

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

int is not correct; here and in many other places. A Sage Integer is definitely allowed.
I'd suggest to use the numeric ABCs; https://docs.python.org/3/library/numbers.html

Copy link
Contributor Author

Choose a reason for hiding this comment

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

these abc's are not supported (in the way you want) by pyright and mypy, see microsoft/pyright#1575 and references therein. We also cannot use Integer since its a cython class and there are no stubs for that (yet). So yeah, its a partial information that is still better than nothing (since ints are allowed).

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm pretty sure that the mantra "don't lie to your static checker" should apply.

Acceptable "partial information" would be using a supertype, not a subtype, of what is allowed. If our Integer cannot be represented, then this will just have to be an Any or object.

Copy link
Contributor

Choose a reason for hiding this comment

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

@tobiasdiez Do you have an opinion on what tools to use for generating the typing stubs (pyi) for our Cython classes? A quick search led me to https://github.com/RaubCamaioni/CythonPEG, but I'm guessing there must be more available

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Acceptable "partial information" would be using a supertype, not a subtype, of what is allowed. If our Integer cannot be represented, then this will just have to be an Any or object.

With any you don't get an error if you pass in a string. With int you can pass in any sage integer (which is treated as any). Since there are so many open challenges with even getting basic type checks to work, the mantra would be more "over time lie less and less to your static checker".

@tobiasdiez Do you have an opinion on what tools to use for generating the typing stubs (pyi) for our Cython classes? A quick search led me to https://github.com/RaubCamaioni/CythonPEG, but I'm guessing there must be more available

There is some movement to implement this directly in Cython. I would wait for this. There is an open issue in the Cython repo (that also compares different workarounds/packages in case you are interested)...

Copy link
Member

Choose a reason for hiding this comment

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

Acceptable "partial information" would be using a supertype, not a subtype, of what is allowed. If our Integer cannot be represented, then this will just have to be an Any or object.

+1

With any you don't get an error if you pass in a string. With int you can pass in any sage integer (which is treated as any). Since there are so many open challenges with even getting basic type checks to work, the mantra would be more "over time lie less and less to your static checker".

It's not only that you are lying to your static checker: you are also lying to the user/developer examining the source code. IMHO, no typing information is better than a false one.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Note that the objective of these type checks is to discover bugs. In this case, that you get notified about passing something wrong into the method. The idea that the types should strictly represent the runtime behavior is not very helpful. The numpy docs for example state

NumPy is very flexible. Trying to describe the full range of possibilities statically would result in types that are not very helpful. For that reason, the typed NumPy API is often stricter than the runtime NumPy API.

But yes, I agree that finding a solution for the integers would be nice. Let me think about it (and propose ideas in case you have some).

@@ -3448,7 +3552,7 @@ def __eq__(self, other):
and (self._chart2 == other._chart2)
and (self._transf == other._transf))

def __ne__(self, other):
def __ne__(self, other: CoordChange) -> bool:
Copy link
Contributor

Choose a reason for hiding this comment

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

Likewise here

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants