-
-
Notifications
You must be signed in to change notification settings - Fork 9.7k
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
BUG: Adds support for array parameter declaration in fortran code #15457
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for working on this! I agree that the PR could be split into several smaller ones each fixing one issue at the time. Otherwise, PR-s may grow with no limit.
Hi all, I just added a few comments to let you know that I'm planning on working on more delicate issues in separate PR's so we can do proper testing and review. The code above works for simple cases. I don't know if this process is the recommended one, let me know what you think. Thanks! |
Just to be clear: you want to merge this as-is with the problematic indexing? If so, could you add an |
My understanding was that #15706 was extracted from this, so should be merged first. Edit: Looks like they were made orthogonal, so the order matters less |
5d0f802
to
9356efc
Compare
Sorry, this was a bit messy but I think I have a better version now. I also added two tests, one which is meant to record the fact that for now we can't parse multidimensional parameter arrays, since this would imply executing fortran code before the extension module is built (e.g. the intrinsic The code changed quite a bit because I tried incorporating all suggestions while also improving my own understanding of how crackfortran works. Thanks for reviewing and for the patience in waiting for the fix! |
@pearu can you take a look when you have the time? Thanks! |
numpy/f2py/crackfortran.py
Outdated
# This is an array parameter. | ||
# First, we parse the dimension information | ||
a = [item[9:] for item in varsn['attrspec'] if item[:9] == 'dimension'][0] | ||
dimrange = a.lstrip('(').rstrip(')') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
probably safer as
dimrange = a.lstrip('(').rstrip(')') | |
a = a.strip() # does fortran care about whitespace? | |
if a[0] != "(" or a[-1] != ")": | |
raise ValueError(...) | |
dimrange = a[1:-1] |
Otherwise you could strip off unbalanced parentheses
numpy/f2py/crackfortran.py
Outdated
# is often incompatible with the original fortran indexing) | ||
# 2) allow the parameter array to be accessed in python as a dictionary with fortran | ||
# indices as keys | ||
# We are choosing 2 for now. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One option here would be:
class FortranParamArray:
def __init__(self, indices, values):
self._indices = indices
self._values = values
def __getitem__(self, index):
return self._values[self._indices.index(index)]
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And pass the range
object in as the indices
argument.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't consider this because the f2py code does not use OOP and I think this might be a part of a larger refactor in the future.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, @melissawm for developing param_eval
!
My only concern is that it is too rigorous in reporting unsupported (read: notimplemented) features as failures while f2py may not need these features when creating the wrapper code.
The used naming convention in "crackfortran.py" has an actual purpose: be forgiving on things that do not matter :)
numpy/f2py/tests/test_parameter.py
Outdated
y = self.module.foo_array_any_index(x) | ||
assert_equal(y, x.reshape((2,3), order='F')) | ||
|
||
def test_constant_array_multidimensional(self): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why this test is expected to fail? f2py should pick up only the following information from the code:
subroutine foo_multidimensional_array(x)
 integer, parameter:: dp = selected_real_kind(15)
 real(dp), intent(inout):: x(5, 3, 2)
 end subroutine
The parameter y
is irrelevant here and all the issues with it should be ignored by f2py.
numpy/f2py/crackfortran.py
Outdated
@@ -2873,6 +2906,84 @@ def analyzevars(block): | |||
analyzeargs_re_1 = re.compile(r'\A[a-z]+[\w$]*\Z', re.I) | |||
|
|||
|
|||
def param_eval(n, varsn, v, g_params, params): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
param_eval
is a complicated function and deserves explicit testing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let me know if the tests I added are enough.
numpy/f2py/crackfortran.py
Outdated
d = str(params[d]) | ||
if d[:d.find("(")] in params: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit1: a white space before (
in d
would break this code.
nit2: consider
>>> d='abc'
>>> d[:d.find('(')]
'ab'
Hence, d[:d.find("(")] in params
is buggy. Suggest checking .find(..)
return value against -1
before continuing.
numpy/f2py/crackfortran.py
Outdated
except Exception as msg: | ||
params[n] = v | ||
outmess('get_parameters: got "%s" on %s\n' % (msg, repr(v))) | ||
params[n] = param_eval(n, vars[n], v, g_params, params) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since param_eval
does not implement the corresponding Fortran specification in full, and often it does not matter (see test_constant_array_multidimensional), the try-except block should remain.
numpy/f2py/crackfortran.py
Outdated
dimrange = range(int(dimrange[0]), int(dimrange[1])+1) | ||
else: | ||
print(n) | ||
raise Exception('param_eval: multidimensional array parameters not supported: %s\n' % repr(n)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would just report this via outmess
rather than raising an exception because the corresponding case is often irrelevant for the f2py task.
I think I addressed everything, feedback is appreciated. Thanks once again for the patience and for the time spent reviewing this! |
numpy/f2py/crackfortran.py
Outdated
' supported: %s\n' % repr(n)) | ||
|
||
# Parse parameter value | ||
v = v.lstrip('(/').rstrip('/)').split(',') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
strip is likely not suitable here, it maps (/(a), b/)
to a), b
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I updated the code to address this, but I'm not sure if another approach would be preferable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
v = (v[2:-2] if v.startswith('(/') else v).split(',')
numpy/f2py/crackfortran.py
Outdated
|
||
then `d = 2` and we return immediately, with | ||
|
||
`param_parse(d, params) = '2'` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you write this in the form of a doctest, with >>>
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let me know if this is what you had in mind. I could write a proper doctest but that would involve populating the entire param
dictionary which I don't think is the idea. I tried looking for similar tests in the rest of the code but couldn't find any. Thanks again for your patience on this, I'm still learning! Feel free to keep commenting if you think anything can be improved.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've put a suggestion above - but yes, a minimally populated params dictionary is exactly what I'm looking for, since otherwise it's hard to tell what form that argument takes.
080f874
to
fe0d634
Compare
(Sorry for the mess in the history - I didn't squash yet because it might make it harder to review. I'll do that once this is ready to merge) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Found one bug and have a few nits. Otherwise, looks good!
d953599
to
68e308e
Compare
@melissawm could you squash this before I rebase it? |
68e308e
to
9e792eb
Compare
Done, thanks @HaoZeke |
Thanks @melissawm. This actually rebases cleanly, the relevant branch is here. So: # Grab the F2PY refactor PR
gh pr checkout 20481
# Add it to your fork, assumes origin to be the fork
git push origin HEAD:f2pyRefactor
# Goto your branch for this PR
git checkout f2py-param-fix
# Rebase onto the refactor
git rebase -i f2pyRefactor
# Push the updated branch
git push -f In case it does not apply cleanly, the other option is to checkout the branch mentioned above and force push that to this PR. Let me know if this can be made clearer for the other PRs :) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps a release note for param_eval
would be nice, otherwise this is ready.
@melissawm Needs rebase. Seems this was about ready to go. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This needs some work after the changes introduced in the recent PRs, but the issue still exists.
@melissawm what's the status of this PR? It seems it was ~finished/ready to go already? |
@melissawm Is this still in progress? @pearu @HaoZeke Have your requested changes been dealt with? |
Needs rebase. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This works, and fixes the still present #11612 so it would be great to have this in :)
I think all the requested changes were made, and subsequent iterations can clean up any new issues.
@pearu could you take a look? |
5512958
to
0965749
Compare
Would be nice to have this in :) I think any additional changes can be done in followups if necessary. |
0965749
to
f2b3638
Compare
@pearu could you take a look / remove the review changes requested note? Can't be merged otherwise EDIT: Dismissing the review to get this in :) |
Stale review, changes are good to go.
Hello,
This PR is supposed to add support for array parameter declaration in fortran code. Previously, this resulted in an error when trying to use array parameter values to define other variables in the same code (see bug #11612)
I also added some tests, including one which is supposed to fail right now because this fix does not include the special case of array parameters with 0-based indexing in fortran.
Closes #11612
Closes #8730