-
Notifications
You must be signed in to change notification settings - Fork 10.6k
[iterator] Use iter and next to loop through Python Object (TF-337) #23268
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
Conversation
stdlib/public/Python/Python.swift
Outdated
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.
Is it necessary to delete this? I think your change should be purely additive.
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.
Interesting, without removing this change It is falling back to the same iteration. I might be doing something wrong here.
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.
In think you would need to implement:
public func makeIterator() -> PythonObject { return Python.iter(self) }
as part of a conformance to Sequence?
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.
Adding Sequence conformance works. Thanks 👍
stdlib/public/Python/Python.swift
Outdated
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.
Hmm, this is looking simpler:
guard let result: OwnedPyObjectPointer = PyIter_Next(obj.borrowedPyObject) else {
try! throwPythonErrorIfPresent()
return nil
}
return PythonObject(owning: result)
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 is exactly what I had initially, but I refrained from the straight C-API calls.
stdlib/public/Python/Python.swift
Outdated
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.
Clean these up because they aren't used?
stdlib/public/Python/Python.swift
Outdated
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 believe the prevailing syntax is "ClassName : Protocol". Please update to that here and for MutableCollection.
|
Looking quite close! |
stdlib/public/Python/Python.swift
Outdated
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.
Is this over 80 columns? If so, can you please make it fit?
stdlib/public/Python/Python.swift
Outdated
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.
Our local convention is to avoid ‘self.’ unless it’s required.
stdlib/public/Python/Python.swift
Outdated
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.
Avoid explicit type annotation when it can be inferred.
test/Python/python_runtime.swift
Outdated
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.
Add a space after each comma.
stdlib/public/Python/Python.swift
Outdated
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 recommend against the technique of making sequences their own iterators too. It's usually a better choice to declare a nested Iterator type, ensuring that the sequence is unambiguously multi-pass with a fresh iterator from the start on each call to makeIterator().
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 am not sure I get this part correctly
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.
You could make a nested Iterator struct which would wrap a PythonObject and just expose next(). This way, it is more type-safe if someone tries to iterate a python sequence themselves.
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.
You can define a ‘struct Iterator : IteratorProtocol {}’ within a PythonObject extension.
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 PR. Is this internal struct okay to be public here?
a94bd94 to
7785ea5
Compare
stdlib/public/Python/Python.swift
Outdated
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 property should be private or fileprivate because it is only used within this extension.
stdlib/public/Python/Python.swift
Outdated
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.
Please rename this to something with less abbreviation. How about pythonIterator?
| let pyObjIterator: PythonObject | |
| let pythonIterator: PythonObject |
|
Thanks @rxwei 👍 for the comments |
stdlib/public/Python/Python.swift
Outdated
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 think this can be simplified to use ‘Python.next()’ directly. Have you tried that?
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.
There’s of course some trade off between efficiency and convenience. I think consistency is key here—we should either use pure C APIs for ‘makeIterator’ and ‘next’, or pure Python APIs.
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.
In fact, that was my first commit. I think it is better to stick with one [edited] APIs, rather than mix and match. #23268 (comment)
Edit: The problem with Python API is that Python.next and iter will return PythonObject rather than PythonObject? This complicates things, because the error is thrown when the method is called dynamically. I will try to use C-Apis for both
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.
The problem with Python API is that Python.next and iter will return PythonObject rather than PythonObject? This complicates things, because the error is thrown when the method is called dynamically.
This is okay--you can check whether the python object is equal to Python.None. However, I like using C APIs for both since that's strictly more efficient :)
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 contributing!
stdlib/public/Python/Python.swift
Outdated
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.
Our local convention is to omit self. unless it's required.
| let result = PyObject_GetIter(self.borrowedPyObject) | |
| let result = PyObject_GetIter(borrowedPyObject) |
stdlib/public/Python/Python.swift
Outdated
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 could cause a segfault. PythonObject.init(consuming:) already takes ownership of a PyObject pointer and calls Py_DecRef in its deinit. Remove this defer block and then you are good.
Also, there's no need to add empty lines in this compact function.
stdlib/public/Python/Python.swift
Outdated
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.
If PyObject_GetIter returns nil (aka, a NULL pointer), this would be a precondition failure and a Python TypeError must have been raised, according to PyObject_GetIter's documentation). Instead of force-unwrapping result, we should use a guard statement and throw the Python exception.
guard let result = PyObject_GetIter(borrowedPyObject) else {
try! throwPythonErrorIfPresent()
// Unreachable. A Python `TypeError` must have been thrown.
preconditionFailure()
}
return Iterator(pythonIterator: PythonObject(consuming: result))|
Thanks for the awesome help ( I learnt a lot) 👍 |
stdlib/public/Python/Python.swift
Outdated
| try! throwPythonErrorIfPresent() | ||
| return nil | ||
| } | ||
|
|
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 last comment: Could you please remove this empty line?
|
Thanks a lot, @sendilkumarn! |
|
@swift-ci please test tensorflow |
…wiftlang#23268) This adds iterator support for iterating through a `PythonObject`. Makes `PythonObject` conform to `Sequence`. Resolves [TF-337](https://bugs.swift.org/browse/TF-337).
This adds iterator support for iterating through the Python Object.
This replaces Mutable collection with the IteratorProtocol and Sequence.
Resolves TF-337.
P.S. This is my first PR.