#internals-and-peps
1 messages Β· Page 158 of 1
hash(object)```
Return the hash value of the object (if it has one). Hash values are integers. They are used to quickly compare dictionary keys during a dictionary lookup. Numeric values that compare equal have the same hash value (even if they are of different types, as is the case for 1 and 1.0).
Note
For objects with custom `__hash__()` methods, note that [`hash()`](https://docs.python.org/3/library/functions.html#hash "hash") truncates the return value based on the bit width of the host machine. See `__hash__()` for details.
that only affects strings, right?
I think so but you should know that better than me π
it does affects anything that contains strings though
ohh, ty
maybe you could include a == b implies hash(a) == hash(b)
being a core dev doesn't mean I know everything about the runtime π
interesting, ty
https://docs.python.org/3/reference/datamodel.html#object.__hash__ actually has a bunch more information too
oh i never know that this site existed (as in reference), thanks for the info!
i have a good python idea and was thinking of submitting a PEP, can i get some general advice on how to do this?
okie dokie
first run it through python-ideas
oh wait it's already mentioned in the above link
https://github.com/python/cpython/blob/main/Python/hashtable.c#L105-L116= why is it important that the number of buckets be a power of 2?
Python/hashtable.c lines 105 to 116
/* makes sure the real size of the buckets array is a power of 2 */
static size_t
round_size(size_t s)
{
size_t i;
if (s < HASHTABLE_MIN_SIZE)
return HASHTABLE_MIN_SIZE;
i = 1;
while (i < s)
i <<= 1;
return i;
}```
I think to make this line efficient: https://github.com/python/cpython/blob/1c8b3b5d66a629258f1db16939b996264a8b9c37/Python/hashtable.c#L241
Python/hashtable.c line 241
size_t index = entry->key_hash & (ht->nbuckets - 1);```
i have no idea what it means to & two ints π
bitwise and
it's basically modulo, but much more efficient because it's a much simpler operation at the CPU level
huh
(bitwise and in general is not like modulo, but anding with a power of two minus 1 is)
wow that is stupidly clever
it took me a second
x = 0b1010111010001110101
y = 0b0000000000001110101
z = x & y
wait, i actually don't think i get how it works
wouldn't that always just give 0b1110101?
let's assume 8-bit integers
and our hash table is size 8
so then nbuckets - 1 = 7 = 0b0000111
anything & 0b0000111 means we basically set the first five bits to 0
ohhhhhh
so you get out a value that has only bits within the last three set, ergo it's within range(8)
ok so i did get it right, i just picked a weird example
and the point of making it a power of 2 is that you get a number where all the trailing bits are 1
whereas in the y example above, it doesn't work as modulus because there's a 0 in the trailing bits
whereas
x = 0b1010111010001110101
y = 0b0000000000001111111
z = x & y
would work, basically masking off the higher bits
dang
c people are smart
ty for explaining
and how does one decide on numbers like this?
#define HASHTABLE_MIN_SIZE 16
#define HASHTABLE_HIGH 0.50
#define HASHTABLE_LOW 0.10
#define HASHTABLE_REHASH_FACTOR 2.0 / (HASHTABLE_LOW + HASHTABLE_HIGH)
experimentation? some rule of thumb in a textbook?
probably some of both, plus Tim Peters's intuition
oh, is this hashtable only used for tracemalloc?
Yes.
Dictionaries are implemented in Objects/dictobject
And that implementation is (1) heavily commented and (2) has some super interesting supporting notes, both of which discuss how they landed on the specific behaviours that they did.
https://github.com/python/cpython/blob/1c8b3b5d66a629258f1db16939b996264a8b9c37/Objects/dictnotes.txt#L120
https://github.com/python/cpython/blob/1c8b3b5d66a629258f1db16939b996264a8b9c37/Objects/dictobject.c
Objects/dictnotes.txt line 120
Results of Cache Locality Experiments```
oh my mistake! thanks for the pointer
How do I interact with issues while bpo is being migrated?
Do I just take a break? π
you take a break π
caffeine and adhd
Money.
excitement and fun
a combination of the previous three responses
Interesting project or an impending deadline
honestly pep 9001 had some good stuff
like higher line limits
(before your ide starts yelling at you)
usually I just start writing when I feel like it, and if I like what I'm doing or I'm debugging I'll end up coding for a while
coding around 4-6 hours consecutively fries my brain though, so I'd watch out for burnout
so does watching noobs in -general.. poor mods :p
https://github.com/python/cpython/pull/28342
Why doesnβt this change appear in string.py on my computer, even though it appears in string.py on GitHub?
In string.py, the capwords function passes str.join a generator expression, but the map function
could be used instead. This is how capwords is currently written:
def capwords(s, sep=None):
&q...
Have you pulled the most recent changes?
I assume that you have a fork? Then the origin, which is what git pull usually synchronizes from, is out of date.
You need to update your fork
this sounds wild out of context
Hello guys, I have a task at university, which I think is basically to make some implementation of something with the use of SciPy (sparse matrices). Do you have any topic and resources on that which can be helpful for my task :D. Thank you in advance!
It's not in 3.10
both? The release is generated from the code on github
https://github.com/python/cpython/blob/main/Lib/string.py
It shows up here
That's main (i.e. 3.11 pre-releases), you can see that the 3.10 branch still uses the previous version https://github.com/python/cpython/blob/89697f7374ea947ebe8e36131e2d3e21fff6fa1d/Lib/string.py#L37-L48
Lib/string.py lines 37 to 48
def capwords(s, sep=None):
"""capwords(s [,sep]) -> string
Split the argument into words using split, capitalize each
word using capitalize, and join the capitalized words using
join. If the optional second argument sep is absent or None,
runs of whitespace characters are replaced by a single space
and leading and trailing whitespace are removed, otherwise
sep is used to split and join the words.
"""
return (sep or ' ').join(x.capitalize() for x in s.split(sep))```
it will appear in 3.11
Eventually a 3.11 branch will be cut off of the main branch, and work for stuff that will go into 3.12 or later will continue on main, and fixes that are specific to 3.11 will go into the 3.11 branch.
Alright, thatβs good. Then I hope they donβt decide to change back before 3.11 is released.
Funny to read whats going on here :)
huh, do changes like this really get accepted into CPython?
sounds really minor
a bunch of these are left around as things that newer contributors can take care of IIRC
and in some cases they may not be left around intentionally, but a beneficial change may still be discovered, even if it's pretty minor
tiny improvements like that aren't necessarily encouraged, but as long as they have a clear benefit they'll be accepted
yep
like create new functions in Objects/longobject.c only for modulo/remainder operations instead of just using divmod with NULL
generally, changes will be accepted if they bring more benefit than cost.
if you make something that's a tiny improvement that's hard to verify or test, or that requires a lot of reviewer time, it might not get accepted
larger improvements with clearer benefits, or changes that are easier to prove correct, are more likely to be accepted.
i consider it a tiny improvement that there was a iirc 2% general speedup that this brought in exchange for 3 new functions
So, if you're have a look at GitHub you can watch all the issues appearing in real time.
Well, here they are: https://github.com/python/issues-test-cpython/issues
Why is it possible to assign a variable to function?
In [16]: def fun():
...: x = 1
...: return x
...:
In [17]: fun.x
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
Input In [17], in <cell line: 1>()
----> 1 fun.x
AttributeError: 'function' object has no attribute 'x'
In [18]: fun.x = 2
In [19]: fun.x
Out[19]: 2
it's called "assigning to a function attribute" and i'm not sure why they decided to allow that
Because functions are first class objects too
Python 3.11.0a6+ (heads/main:9e88b572fb, Apr 10 2022, 10:41:18) [GCC 11.1.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> def function():
... return 1
...
>>> dir(function)
['__annotations__', '__builtins__', '__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__get__', '__getattribute__', '__globals__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__kwdefaults__', '__le__', '__lt__', '__module__', '__name__', '__ne__', '__new__', '__qualname__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']
>>>
right, but that doesnt necessarily mean it has to support setting attributes on them
eg x = object(); x.a = 1 fails
Then the answer is that it hasn't been disallowed
I don't know the specific implementation for functions, but for items implemented in C and python, there are explicit checks and specific errors that are written when setting attributes
See here for example: https://github.com/python/cpython/blob/main/Objects/funcobject.c
there is no specific implementation
it uses the generic one in https://github.com/python/cpython/blob/main/Objects/typeobject.c#L3988-L4042
more specifically in https://github.com/python/cpython/blob/main/Objects/object.c#L1365-L1442
Then that's the full answer. Function objects use the generic function for setting attributes which doesn't disallow setting attributes with arbitrary names
They have a __dict__ which allows arbitrary attributes as it can be convenient to store data on them
I have seen code that actually relies on this
i don't remember what the purpose was, but the author of the program actually used custom function attributes to pass information around
i remember at the time thinking it was confusing and unnecessarily obscure software design, but i don't remember what exactly they were doing
file under "just because you can, doesn't mean you should"
<@&831776746206265384> ^
@humble phoenix Please don't post random TikTok stuff. Respect the channel topics.
aiohttp used to set a special attribute on middlewares
the stdlib does it too: https://github.com/python/cpython/blob/main/Lib/typing.py#L2487
Lib/typing.py line 2487
f.__final__ = True```
interesting!
Another example is lru_cache, which adds a function to clear the cache and another to return statistics.
mildly interesting discovery: the error messages for using break and continue outside loops are a bit different
SyntaxError: 'break' outside loop
SyntaxError: 'continue' not properly in loop
yield and return outside functions are similar to break
I'm listed as a mannequin on the github issues, is this permanent?
By BPO account is linked to my github account
i believe you're listed as a mannequin because your github account didn't actually make those comments
if you make a comment on a github issue with your account, does that comment show up as a mannequin?
No, it never does
essentially mannequins are used by github when importing data from other sites, so comments that are echoed to github will show up as a mannequin
Will the mannequin change to be my account?
no
can you link to an issue like that?
because I don't think i am marked as a mannequin: https://github.com/python/cpython/issues/90392
something must have been mis-aligned about your identities on bpo and github
Can I un-mis-align it? π π
sorry, i don't know. You could try asking at https://discuss.python.org, there are threads there about the migration
It's not a problem and thanks!
Which category?
Core Development looks right, since they talked about it there: https://discuss.python.org/t/github-issues-migration-status-update/14573/2
Yes, it's permanent. Only people who are members of the python org on GItHub get their identities preserved because of limitations on GH's side
That's why @spark magnet isn't a mannequin
not sure if things will get updated if they join the org, I'd expect not
GitHub Issues are now live! The migration from bugs.python.org to GitHub is now officially complete, and all the issues have been successfully transferred. π₯³ You can now browse, create, and comment on issues here: Issues Β· python/cpython Β· GitHub bugs.python.org will remain available, with the following changes: It will be read-only and it w...
Hey, what would a be polite way to request a review? The cpython PR has been waiting for 2 months
do you have a BPO or an issue made at https://github.com/python/cpython/issues ?
yes
i mean you could mention the person you want to be reviewing your code and ask them
I'm not waiting for anyone in particular, it'd be an initial review
pinging someone at random doesn't seem great
so what's the PR number?
Fancy seeing you here! π
6 16 SETUP_FINALLY 10 (to 38)
what is (to 38) here??
\
It shows that the finally block goes from index 10 to 38
PEP 678 Enriching Exceptions with Notes has been accepted. It adds a way to add arbitrary string notes to exceptions, which are displayed after the exception message in tracebacks.
https://peps.python.org/pep-0678/
i misread it as "added"
pretty cool new changes though
python 3.11 is probably gonna be big
also read that, i'm lacking the imagination as to how that would be useful though
They mentioned examples in the PEP, but it's broadly going to be useful most for frameworks and other sorts of libraries which might want to catch an exception, add some data, then re-raise.
For instance Hypothesis generates sample data to pass to test functions. If the test raises an exception, it can go add a copy of the specific bad values to the exception for you to look at.
It needs to be built into BaseException because any exception could be raised, and you don't know how its __str__ behaves.
ok
not sure why they need an extra dunder for that though
or why they need a PEP for that
Requires a PEP because it's a relatively significant addition to the language
I assume the dunder attribute comes from the need to store extra information while maintaining backwards compatibility
stringly exceptions π
aka the format-all-the-details-into-a-string-so-that-I-have-to-parse-it-with-regex pattern
yay?
yay?
...yay?
ΒΏΚΙΚ

i think the intention is that these are for human readable errors
some libraries do a really good job of adding structured metadata to their exceptions, like jsonschema
fair
I'm looking for guidance on using entry points/console_scripts vs python -m <module> within user level packaging. It seems a few days ago (in GH #24003), the last of the docs direct users of cli's included in Lib to use the python -m <module> syntax. I was wondering is calling python -m <module> going to be recommended for user developed modules too, or should those people stick with entry points. Sadly I can't find much more context even from the bug at bpo-22295. It seems that the work around this started before the now common standard of python3 on unix like distributions, so python is often displayed via argparse, but isn't what was actually called. I'm happy to make a PR related to actually looking at sys.argv[0] if that's what makes sense, but I'm just not sure what the recommendation is for the future.
i don't have any sense that this is going to change in general. i think the docs recommend this specifically because you get so so so many people with "halp it broke" issues, where the problem was that they were invoking their command with the wrong python executable
i don't think there is going to be any general movement against console script entry points. too many programs use it and too many people rely on it
(and people want it)
Right now it's nice that strings like this https://github.com/python/cpython/blob/main/Lib/json/tool.py#L20 are static. However we could detect if python3 was used when the command was called. Leave all the docs the way they are, but allow for help text to match the invocation of the command aka:
me@comp$ python3 -m json.tool -h usage: python -m json.tool [-h] [--sort-keys] [--json-lines] [infile] [outfile] could be dynamic and display
me@comp$ python3 -m json.tool -h usage: python3 -m json.tool [-h] [--sort-keys] [--json-lines] [infile] [outfile]
Lib/json/tool.py line 20
prog = 'python -m json.tool'```
It might just be nice for users to be able to copy the help/usage and use it directly
Also, note I'm not asking for a feature, I could do this easily enough if people think it makes sense.
that would be nice. i think even click requires you to hard-code your app name? i don't remember off the top of my head
maybe it'd be a good snippet to post online or even on pypi?
it'd be nice to have it in the standard libraries
django does something like this I believe
I'll take a look in their source, and see if I can find an example.
I might just make a PR for people to view and discuss, and a bug on the bug tracker too. I think it's the best way to get a wider discussion going. Since the django example doesn't show what I expected but is here for reference https://github.com/django/django/blob/main/django/core/management/__init__.py#L208-L210
django/core/management/__init__.py lines 208 to 210
self.prog_name = os.path.basename(self.argv[0])
if self.prog_name == "__main__.py":
self.prog_name = "python -m django"```
what if it was like this?
self.prog_name = os.path.basename(self.argv[0])
executable = sys.executable or 'python'
if self.prog_name == "__main__.py":
self.prog_name = f"{shlex.quote(executable)} -m django"
apparently (and unfortunately) it's impossible to get the "real" module name when it's invoked as __main__, without manually parsing __file__, which would require you to manually figure out where the top-level package is, which would be horrible to do
I like that, but are there any weird cases when inside a virtualenv? I might have to test that one.
shouldn't be, sys.executable ought to point to whatever python was used to invoke the program
the real problem is that not all programs have identical behavior between foo and python -m foo
That's kinda back to my original question since I think all things within the standard library are now invoked via python -m foo so no worries there. However for user written modules I would suggest choosing one way of invoking via either via python -m foo or just foo and stick with it. The docs just don't say that yet, and maybe they should. Or maybe my idea is wrong and having a simple pattern (similar to what you suggested above) to handle everything is a better way forward.
imo it's a good idea to support both in general
if you won't support foo then at least support python -m foo
e.g. debugpy only supports the latter
i think the former should always be a synonym for the latter, there's no reason to do it otherwise except to confuse and punish your users
Interesting, that pip supports this kinda, but it doesn't use os.path.basename https://github.com/pypa/pip/blob/3820b0e52c7fed2b2c43ba731b718f316e6816d1/src/pip/_internal/utils/misc.py#L109
src/pip/_internal/utils/misc.py line 109
def get_prog() -> str:```
I get Usage: /usr/bin/python3 -m pip <command> [options] when I call it with python3 -m pip
that makes sense too
they are really trying to emphasize which python you invoked pip with
haha, I mean it does help if you called it within a virtualenv. I might try to propose something that does that for things like json.tool I might have to consider how to do it without repeating the same logic all over the place... Which might actually mean a argparse change that everything could just toggle.
indeed, i was going to say that this should either be a utility function or part of argparse or both
I gotta drop for a bit, but I don't think I'll know which way to recommend without trying to code it up. But thanks for discussing it with me @paper echo . It was really helpfu. I hope to get a PR and bug out in the next day or so. If you would like I can try to drop you a DM with the link once I do.
yeah that'd be nice! or just @ me here
Bump, pinging a python member at random does not seem a great idea, but not sure what else to do, for ref PR is https://github.com/python/cpython/pull/30108
i'll review it
(not right now though)
Thank you, no rush it was more about the process in those cases
I would like to get eventually to a state where cpython PRs are all reviewed promptly, but right now there is 1.6k open ones π¦
Hi, I'm not sure if this should be in a help channel, but it does relate to Python's implementations of floating-point arithmetic.
I'm writing a paper in a class of mine about how Python performs addition between two floating point numbers and analyzing its source code. I'm actually not quite sure where to look for the + operator method and I'm also not sure if this goes deeper into how C performs floating-point arithmetic. Is there any good resources or does anyone have pointers on where to start looking (beyond the GitHub page for python)
It's not always easy to find. In the cpython repo, most builtin types T are in a file called Objects/Tobject.c. So in this case you want https://github.com/python/cpython/blob/main/Objects/floatobject.c
Then addition is implemented by a "slot" (a function pointer in C) called nb_add. If you search for nb_add in that file, you'll find that it's set to a function calledd float_add. It lives here: https://github.com/python/cpython/blob/474fdbe9e4a2ff90ef39e8748da644c86a200981/Objects/floatobject.c#L577
Objects/floatobject.c line 577
float_add(PyObject *v, PyObject *w)```
you can see that in the end, it really does just do C-level float addition π
Thank you so much for the fast response. This should be enough for me to go off of, thank you so much!
how does cpython define "whitespace" in the context of str.strip? https://docs.python.org/3/library/stdtypes.html#str.strip
i noticed that it can strip funky unicode whitespace and i wanted to know what is/isn't valid
!e ```python
assert "\u1680".strip() == ""
@paper echo :warning: Your eval job has completed with return code 0.
[No output]
Can sombody pls add me?
i got as far as here https://github.com/python/cpython/blob/d6fb104690cdeeea04ecbaf5c9bcafc622e03648/Objects/clinic/unicodeobject.c.h#L330 and got stuck
Objects/clinic/unicodeobject.c.h line 330
unicode_isspace_impl(PyObject *self);```
iirc it's something in the header
Include/cpython/unicodeobject.h lines 24 to 25
#define Py_UNICODE_ISSPACE(ch) \
((Py_UCS4)(ch) < 128U ? _Py_ascii_whitespace[(ch)] : _PyUnicode_IsWhitespace(ch))```
ahh ty
/* Returns 1 for Unicode characters having the bidirectional
* type 'WS', 'B' or 'S' or the category 'Zs', 0 otherwise.
*/
thanks @rose schooner !
_Py_ascii_whitespace here https://github.com/python/cpython/blob/main/Objects/unicodeobject.c#L313-L342
basically a LUT for whitespace
np
i was thinking maybe also make a LUT for the unicode whitespace but i realized the vastness of it
so much so that it can't fit inside a LUT
there is one too, @paper echo linked it above. it's only ~30 chars
yes i've seen that
though that file might be autogenerated
Objects/unicodetype_db.h line 1
/* this file was generated by Tools/unicode/makeunicodedata.py 3.3 */```
import unicodedata
def is_whitespace(c: str) -> bool:
if len(c) != 1:
raise ValueError("Must be a length-one string.")
return (
c in {"\t", "\n", "\x0b", "\x0c", "\r", "\x1c", "\x1d", "\x1e", "\x1f", " "} or
unicodedata.bidirectional(c) in {"WS", "B", "S"} or
unicodedata.category == "Zs"
)
that's the python translation of it, i think
not quite
it's also implemented for multiple-character strings
translates to ```py
def _isspace(self: str) -> bool:
return (
c in {"\t", "\n", "\x0b", "\x0c", "\r", "\x1c", "\x1d", "\x1e", "\x1f", " "} or
unicodedata.bidirectional(c) in {"WS", "B", "S"} or
unicodedata.category == "Zs"
)
def isspace(self: str) -> bool:
length = len(self)
if length == 1:
return _isspace(c)
if length == 0:
return False
for i in range(length):
c = self[i]
if not _isspace(c):
return False
return True
good catch, thanks
the logic is still the same though right?
probably
merp
I was looking at asyncio.Semaphore and i realise it would probably work for one of my needs, but i need to see how many are remaining, which is not part of the documented api
the other problem is subclassing doesn't seem like it would work since semaphore inherits from private asyncio classes
my question is, what is the practice for a private attribute that i want to use?
should I use it?
the problem is this is in a library
I have
@unittest.skipIf(sys.platform == "win32", "Requires file permissions")
class TestsPermissionExtraction(unittest.TestCase):
...
@unittest.skipUnless(os.getuid() == 0, "requires root")
def test_extractall_preserve_all(self):
...
It fails on windows because getuid isn't provided
It makes sense because to evaluate the class definition, they decorator funtion has to be executed
you could do this py if sys.platform == "win32": os_getuid = lambda: 1 else: os_getuid = os.getuid before the class and use ```py
@unittest.skipUnless(os_getuid() == 0, "requires root")
def test_extractall_preserve_all(self):
...
Thanks, I'll do that
ok but wait i think i see a problem
does unittest.skipUnless raise an error if the condition is false
ok good
wait where do i write these python exams?
is there a guide on writing good peps
I think you need a cpython core dev to sponsor your PEP. if you want to suggest a change to the language as an "outsider", you can go to the python-ideas email list. PEPs are occasionally borne of discussions there, though PEPs in general are few as compared to the number of ideas thrown out.
0.o i didnt know about that, interesting
anyone who wants to write a PEP with me to introduce the triple *** for arrow notation? :>
can you explain what that means?
ah yes
* is multiplication, ** is exponentiation, so logically *** would be tetration or up-arrow
too bad it would require operating on operators.. or somesuch π¦
maybe it could be implemented as a recursive closure
like a *** b only returns a function, which can be stacked with another *** c - so all *** operators in a term form a single one
dafuq is this
^^^
never heard of ***
That's the point
In other languages ^ is exponentiation ... *** does make sense in the context of Python
What about ^^ if *** isnt feasible.... at least it will be distinguishable from xor
This MATLAB function raises each element of A to the corresponding powers in B.
What is involved in writing a PEP lmao never wrote one?
Not sure what the use of tetration would be if the result usually doesn't fit in a float :)
never wrote one, nope
https://peps.python.org/pep-0001/#start-with-an-idea-for-python is the documentation, fyi. For what it's worth I see very little use for a tetration operator in the core. Wouldn't it usually create numbers that are too big for the builtin int type to represent?
Python Enhancement Proposals (PEPs)
last time i experimented with tetration it worked okay-ish, just trading speed for arbitrary sizes - i think it's using a str representation of numbers at some point
what is "it" here? I don't think tetration is supported by the standard library
oh right, tetration doesn't go up as fast as I thought it would so it's still representable
still, you'll have a hard sell convincing people that tetration is a common enough operation to merit a new operator
yeah π¦
- not even sure where to use tetration in the wild 2) not sure how to calculate it efficiently, if ever
but it'd be cool, you have to admit
we could expand it to a hyperoperation ternary syntax
yesss π
but i'm pretty sure people only need the first 3 hyperoperations
then 4 and above (tetration+) doesn't seem to have any use
there's also the fact they're gonna be expensive
yeah.. maybe there's an application in statistics or so? i could imagine that stochastics might deal with huge numbers
but they usually don't try to calculate the numbers but solve things algebraic
Very often when considering proposals it's useful to see how code is currently being written to gauge the benefit from adding a new feature.
I wanted to evaluate a similar proposal to this one https://mail.python.org/archives/list/python-ideas@python.org/thread/VLI3DOFA5VWMGJMJGRDC7JZTRKEPPZNU/#VLI3DOFA5VWMGJMJGRDC7JZTRKEPPZNU that was raised elsewhere but I got sidetracked and ended up creating a new project.
I wonder if anyone would find this useful
The use-case I gave is very niche π
The above example matches files that import the ast library. Queries can already be quite complex but I want to work on making them easier to write and I'm going to add statistical queries so you can work out how often certain types of code are being written
Check in niche langs and see if they have the operator if not it may be even harder to justify
this is what dataclasses, attrs, and to some extent pydantic are for
i see that they were discussed in the mailing lists, but i guess i don't see the difference still
does pip actually use the build backend to install a package when a wheel is available? or does it have its own mechanism to unpack and install the files in the wheel, without using any build backend like setuptools?
Dataclasses are slightly different (__init__ would be overritten entirely) and attrs isn't stdlib. The proposal I was looking at was similar to the one I linked but not the same, it's syntactic support to automatically initialise marked attributes in the __init__ method. Like the following
class ExampleClass:
def __init__(self, these, are, example, parameters, word):
self.these = these
self.are = are
self.example = example
self.parameters = parameters
do_something(word)
Which would be written as the following
class ExampleClass:
def __init__(self, @these, @are, @example, @parameters, word):
do_something(word)
I'm not too concerned with the linked proposal itself I just wanted to show my new tangentially related toy project that the proposal inspired me to make π
But I do like the new syntax, I am waiting for it to be posted to python-ideas, if it doesnt happen then I'll do it myself and see how others feel about it.
Pretty sure the thing with the wheel is that it is already built and everything? So- no need?
!pep 654
how does this pep work
i see. but i don't understand how this use case justifies new syntax and language semantics. attrs for example has a "post init" method that would work here
The extra word argument that isn't being directly assigned to anything but is still accepted in the init function might be a problem with this
i also like it
β«but how would you do it if it was ever accepted
I don't think it would be too difficult to implement. I'd have to see how __init__ is generally handled because I haven't done any work there yet but my idea is to perform the assignments before the rest of the initialisation code.
There are some other considerations too like typing that need to be worked out
i'm not even sure how it would be implemented
it's as hard as trying to think of how to implement implicit self
Maybe there's some context to this that I'm unaware of that makes this difficult?
oh yeah these things also rely on another argument before it
Oh, yes. With that in mind, does what I'm saying now make sense?
i guess
Ok, at least the implementation is plausible π
oh, i see. that's fair, and yes it's an annoyance with attrs/dataclass
i'm not convinced though
normally in this situation i require an alternate constructor to make an instance
the "main" __init__ is reserved for "internal" use only
so you'd do MyThing.new(...) instead of MyThing()
!timeit x=2
@sturdy timber :white_check_mark: Your timeit job has completed with return code 0.
5000000 loops, best of 5: 52 nsec per loop
!timeit (x:=2)
@sturdy timber :white_check_mark: Your timeit job has completed with return code 0.
10000000 loops, best of 5: 31.7 nsec per loop
!e ```python
import dis
print("x=2:")
dis.dis("x=2")
print("(x:=2):")
dis.dis("(x:=2)")
@sturdy timber :white_check_mark: Your eval job has completed with return code 0.
001 | x=2:
002 | 1 0 LOAD_CONST 0 (2)
003 | 2 STORE_NAME 0 (x)
004 | 4 LOAD_CONST 1 (None)
005 | 6 RETURN_VALUE
006 | (x:=2):
007 | 1 0 LOAD_CONST 0 (2)
008 | 2 DUP_TOP
009 | 4 STORE_NAME 0 (x)
010 | 6 RETURN_VALUE
I think it's the LOAD_CONST (None) and RETURN_VALUE for x=2 which only there because it's the end of the code? Whilst the walrus DUP_TOPed so didn't need to load None to return.
So if you did a sequence of instructions then x=2 would be faster
Agree, presumably DUP_TOP is faster than LOAD_CONST None
ah i c
how would that be faster?
Then x=2 wouldn't have to do LOAD_CONST None
That is fair
I think a lot of people don't do this however (I don't for example)
Oh, are you talking about when using attrs?
If I was going to use attrs, I would probably do the same
I've also improved my query tool
I picked the top 4 downloaded packages from pypi and put them in the sources directory
yeah, but also as a workaround in general. i have come to enjoy thinking of __init__ as just "filling slots" so to speak. the only annoyance is that it isn't (as far as i know) possible to mark an annotated attribute as excluded from init
init=False in dataclasses?
aha, attrs has that too
great, problem solved then
im not sure if it allows you to have that without a default value
so ok, fair. there is a use case for this
but does it justify brand new syntax and a very very special case in the language semantics? im not sure
i'd rather see things like with and except suffix expressions, null-safe binary operators and attribute lookups, etc.
@sturdy timber doesn't seem to be faster anymore for python 3.11.0a7: ```py
import dis
print("x=2:");dis.dis("x=2");print("(x:=2):");dis.dis("(x:=2)")
x=2:
0 RESUME 0
1 2 LOAD_CONST 0 (2)
4 STORE_NAME 0 (x)
6 LOAD_CONST 1 (None)
8 RETURN_VALUE
(x:=2):
0 RESUME 0
1 2 LOAD_CONST 0 (2)
4 COPY 1
6 STORE_NAME 0 (x)
8 RETURN_VALUE
>>> from timeit import main
>>> main(["x=2"])
20000000 loops, best of 5: 15.8 nsec per loop
>>> main(["(x:=2)"])
10000000 loops, best of 5: 21.4 nsec per loop
or wait π€
how does the bot time it
!e you're not actually comparing the actual statement code which would make x=2 emerge as the winner ```py
import dis
print("x=2:")
dis.dis(compile("def foo(): x=2", '<1>', 'exec').co_consts[0])
print("(x:=2):")
dis.dis(compile("def foo(): (x:=2)", '<2>', 'exec').co_consts[0])
@rose schooner :white_check_mark: Your eval job has completed with return code 0.
001 | x=2:
002 | 1 0 LOAD_CONST 1 (2)
003 | 2 STORE_FAST 0 (x)
004 | 4 LOAD_CONST 0 (None)
005 | 6 RETURN_VALUE
006 | (x:=2):
007 | 1 0 LOAD_CONST 1 (2)
008 | 2 DUP_TOP
009 | 4 STORE_FAST 0 (x)
010 | 6 POP_TOP
011 | 8 LOAD_CONST 0 (None)
... (truncated - too many lines)
Full output: https://paste.pythondiscord.com/ilogipasic.txt?noredirect
the python bot has some shenanigans going on with timing stuff
For me, the walrus operator is consistently slower
!e
import timeit
print(timeit.timeit("x=2"))
print(timeit.timeit("(x:=2)"))
@west tinsel :white_check_mark: Your eval job has completed with return code 0.
001 | 0.048418553080409765
002 | 0.03021063096821308
it's not "noise"
(x:=2) has 2 more instructions when fairly compared to x=2
!e ```py
from timeit import main
def foo0():
x = 2
def foo1():
(x:=2)
main(['-s', "from main import foo0", "foo0()"])
main(['-s', "from main import foo1", "foo1()"])
@rose schooner :white_check_mark: Your eval job has completed with return code 0.
001 | 2000000 loops, best of 5: 99.9 nsec per loop
002 | 2000000 loops, best of 5: 110 nsec per loop
And above, you can see the walrus operator running faster in one execution
It becomes quite likely when the standard deviation is high or the difference in expected run time is small
IPython also shows that the walrus operator is slower ```py
In [1]: def foo0():
...: x = 2
...: def foo1():
...: (x:=2)
...:
In [2]: %timeit foo0()
71.2 ns Β± 17.5 ps per loop (mean Β± std. dev. of 7 runs, 10000000 loops each)
In [3]: %timeit foo1()
75.8 ns Β± 2.79 ns per loop (mean Β± std. dev. of 7 runs, 10000000 loops each)
by "fairly compared" i mean "properly disposing of the value that (x:=2) returns and making it consistent with x = 2"
!e
import timeit
print(timeit.timeit("x=2;"))
print(timeit.timeit("(x:=2);"))
```is also a fun one
@flat gazelle :white_check_mark: Your eval job has completed with return code 0.
001 | 0.046923632035031915
002 | 0.0305294890422374
of course when the value is actually used then the assignment expression is faster than the assignment statement
why?
LOAD_* + DUP_TOP (COPY 0 in python 3.11) + STORE_* is faster than LOAD_* + STORE_* + LOAD_* for some reason
With
import timeit
print(timeit.timeit("(x:=2);", number=10000000))
print(timeit.timeit("x=2;", number=10000000))
print(timeit.timeit("(x:=2);", number=10000000))
print(timeit.timeit("x=2;", number=10000000))
I'm getting consistently different behaviour running it on the bot vs locally for multiple python versions and I have no clue why. Unless I'm misreading the code it seems the code is put in a loop to be run (https://github.com/python/cpython/blob/35fef2711033ce793ec1cb43dfbd95e2d06ab7bb/Lib/timeit.py#L69-L78) meaning that the walrus should probably be noticeably slower... but no.
Lib/timeit.py lines 69 to 78
template = """
def inner(_it, _timer{init}):
{setup}
_t0 = _timer()
for _i in _it:
{stmt}
pass
_t1 = _timer()
return _t1 - _t0
"""```
what doescompile with co_consts[0] do?
compile the function code
since the first constant is the function code object in that code
ah i c
can we have a frozendict already
!pypi immutables
π
If you don't want a HAMT buy just a read-only mapping, there's types.MappingProxyType
yeah i know it, it's great
too bad it doesn't have an efficient set and list implementations
@grave jolt also I am a bit saddened that x.update(y) is O(len(y)). Does that have to be the case?
!pypi pyrsistent
I don't know to be honest
pyrsistent is not efficient, I think x.add(y) is O(len(x))
From the readme on sets:
Random access and insert is log32(n) where n is the size of the set.
not sure why they specify 32 but yeah
ah sorry, just slower, perhaps not asymptotically.
copying some work of a colleague:
immutables.Map:
m1 = immutables.Map({random.randint(0, 1000000): None for _ in range(1000000)}) # took 2.4s
m2 = m1.update({2000000: None, 2000001: None, 2000002: None}) # took 9.511799999017967e-05s
pyrsistent.s:
s1 = s(*[random.randint(0, 1000000) for _ in range(1000000)]) # took 1.51s
s3 = s2.update([2000000, 2000001, 2000002]) # took 0.24s
so it seems update is not O(1)
also add
if i run
x = [random.randint(0, 1000000) for _ in range(1000000)]
ps = pyrsistent.s(*x)
for i in range(10): ps2 = ps.add(1)
the last line takes about 4s
isn't the point that it's immutable? if you want efficient updates you probably shouldn't be using the immutable version
https://pypi.org/project/frozendict/
implemented in c
or use attrs or pydantics
they can also be frozen and obj attributes are pretty much the same as a dict, if your keys are proper names
frozendict is probably faster though
Iiuc you can get both, clojure data structures do that
#mailing-lists message
isnt this summary broken?
Guess it still looks at BPO instead of GitHub
remember seeing it mentioned in the migration issue
even a PR for it! https://github.com/python/psf-salt/pull/234
hey guys, if a code object uses one byte for each instruction's argument, what happens when a code object is associated with more than 256 names/constants?
what happens when the argument byte needs to exceed 256?
I think there's a special opcode for that, EXTENDED_ARG
!pip chess
EXTENDED_ARG(ext)
Prefixes any opcode which has an argument too big to fit into the default one byte. ext holds an additional byte which act as higher bits in the argument. For each opcode, at most three prefixalEXTENDED_ARGare allowed, forming an argument from two-byte to four-byte.
so now the real question... what happens when a code object is associated with more than 4294967296 names/constants? (\j, but if you do know the answer, i'd greatly appreciate you satisfying my curiosity lol)
also, tysm for answer the original question, this was extremely helpful!
iirc some of us in pydis tested that unrealistic case
the oparg should hold no more than 2147483647 because that's the maximum value
if you extend it may overflow
@nova iris it overflows ```py
import dis
f = lambda:None
f.code = f.code.replace(co_code=bytes([
... dis.opmap['EXTENDED_ARG'], 255,
... dis.opmap['EXTENDED_ARG'], 255,
... dis.opmap['EXTENDED_ARG'], 255,
... dis.opmap['UNPACK_EX'], 255,
... dis.opmap['RETURN_VALUE'], 0
... ]))
dis.dis(f)
1 0 EXTENDED_ARG 255
2 EXTENDED_ARG 65535
4 EXTENDED_ARG 16777215
6 UNPACK_EX -1
8 RETURN_VALUE
a black hole opens and swallows your code
nasal demons
is this supposed to happen ```py
import dis
f = lambda:None
f.code = f.code.replace(co_code=bytes([
... dis.opmap['EXTENDED_ARG'], 255,
... dis.opmap['EXTENDED_ARG'], 255,
... dis.opmap['EXTENDED_ARG'], 255,
... dis.opmap['EXTENDED_ARG'], 0,
... dis.opmap['EXTENDED_ARG'], 0,
... dis.opmap['EXTENDED_ARG'], 0,
... dis.opmap['EXTENDED_ARG'], 0,
... dis.opmap['EXTENDED_ARG'], 0,
... dis.opmap['EXTENDED_ARG'], 0,
... dis.opmap['EXTENDED_ARG'], 0,
... dis.opmap['UNPACK_EX'], 0,
... dis.opmap['RETURN_VALUE'], 0
... ]))
dis.dis(f)
1 0 EXTENDED_ARG 255
2 EXTENDED_ARG 65535
4 EXTENDED_ARG 16777215
6 EXTENDED_ARG -256
8 EXTENDED_ARG -65536
10 EXTENDED_ARG -16777216
12 EXTENDED_ARG -4294967296
14 EXTENDED_ARG -1099511627776
16 EXTENDED_ARG -281474976710656
18 EXTENDED_ARG -72057594037927936
20 UNPACK_EX -18446744073709551616
22 RETURN_VALUE
i'm pretty sure that's outside of the 32-bit signed limit
Not really sure. I know there was a recent bug report about negative extended args
one of the CPython core devs who owns the datetime module is looking for people to share feedback on a proposed change to datetime.datetime.isoformat() - picking a name for a new keyword argument that's being added to that function. If anyone has some time, it would be much appreciated if you could check out https://ganssle.io/s/isoformat-survey and help ensure we pick the best possible name for the new option
That's because the dis module is using a python int not a c int to hold that value afaik
I have a question about the type system
it started out because I wanted to be able to obtain the signature fron all callable members of a BytesIO type
I found that this was not as easy as I thought it would be .. simply calling inspect.signature fails for about half the members.
the object in question is a _io.BufferedReader
for each member, I checked the descriptor defined in each MRO type and then called inspect.signature on that
this worked to get all signatures except write
I thought surely with argument clinic and everything, the signature of write must be part of some metadata somewhere
then I decided to check if all of these members had a docstring and they did, even for write
>>> from _io import BufferedReader
>>> help(BufferedReader.write)
write(...)
Write the given buffer to the IO stream.```
Funny, that method is not in io.BufferedReader.__dict__ and I don't see it in C
trying to figure out where it comes from
well I searched its docstring and a couple places came up - one is a mysterious _pyio module
Lib/_pyio.py:
def write(self, b):
"""Write the given buffer to the IO stream.
Returns the number of bytes written, which may be less than the
length of b in bytes.
"""
self._unsupported("write")``` surely it can't be that
that type us not even in the mro
OK, found it. It's bufferediobase_write , which is METH_VARARGS
but there's a weird registration thing going on with that _pyio one to
io.RawIOBase.register(RawIOBase)
from _io import FileIO
RawIOBase.register(FileIO)```
It looks like BufferedReader doesn't inherit from _BufferedIOBase if you look at bufferedio.c, but elsewhere it does Modules/_io/_iomodule.c: PyBufferedReader_Type.tp_base = &PyBufferedIOBase_Type;
ohh ok
The pyio stuff only gets used if the C implementation is unavailable, which should be basically never
oh that's helpful to know
Actually in this case looks like it's never used
I'm also curious where help() gets the write(...) from
the _pyio one?
yes
io.py unconditionally imports from _io which is the C version
ahh ok
PyDoc_STRVAR(bufferediobase_write_doc,
"Write the given buffer to the IO stream.\n"
"\n"
"Returns the number of bytes written, which is always the length of b\n"
"in bytes.\n"
"\n"
"Raises BlockingIOError if the buffer is full and the\n"
"underlying raw stream cannot accept more data at the moment.\n");
static PyObject *
bufferediobase_write(PyObject *self, PyObject *args)
{
return bufferediobase_unsupported("write");
}```
it does that if it can't find an argspec
that is where you were talking about?
yes
ohh I see so it's just a placeholder
maybe I didn't miss anything then
is there any way to (from python) figure out which C function is associated with one of these members
member/descriptor/...
if I can find its address, I could theoretically figure out what symbol is there maybe
this might help ```>>> io.BufferedReader.write.qualname
'_BufferedIOBase.write'
so that bufferediobase_write should be in the PyType members for something with tp_name of _BufferedIOBase ?
yes
I see in Modules/_io/bufferedio.c a static PyMethodDef bufferediobase_methods[7]; around line 2312
at least there's no magic
yeah so looking in gdb p bufferediobase_methods gives me
[5] = {
ml_name = 0x7ff7ddcd88 "write",
ml_meth = 0x7ff7d072d8 <bufferediobase_write at /data/media/0/src/python3/src/Modules/_io/bufferedio.c:181>,
ml_flags = 1,
ml_doc = 0x7ff7ddd6d0 <bufferediobase_write_doc> "Write the given buffer to the IO stream.\n\nReturns the number of bytes written, which is always the length of b\nin bytes.\n\nRaises BlockingIOError if the buffer is full and the\nunderlying raw stream cannot accept more data at the moment.\n"
}```
theres also a static PyTypeObject *static_types[103]; in object.c and a separate one (static PyTypeObject *static_types[14];) in _iomodule.c lol
that's what static is for π
hehe, indeed
I wonder what PySTEntry_Type is
a symtable entry probably
yes it's in the smaller one .. so I can easily get that type object from python
yes ```PyTypeObject PySTEntry_Type = {
PyVarObject_HEAD_INIT(&PyType_Type, 0)
"symtable entry",
(gdb) p '/data/media/0/src/python3/src/Modules/_io/_iomodule.c'::static_types
$15 = {
[0] = 0x7ff7eb3c10 <PyIOBase_Type>,
[1] = 0x7ff7eb6478 <PyIncrementalNewlineDecoder_Type>,
[2] = 0x7ff7eb4c20 <PyBufferedIOBase_Type>,
[3] = 0x7ff7eb3e48 <PyRawIOBase_Type>,
[4] = 0x7ff7eb61f0 <PyTextIOBase_Type>,
[5] = 0x7ff7eb47c0 <PyBytesIO_Type>,
[6] = 0x7ff7eb5130 <PyBufferedReader_Type>,
[7] = 0x7ff7eb55a0 <PyBufferedWriter_Type>,
[8] = 0x7ff7eb5908 <PyBufferedRWPair_Type>,
[9] = 0x7ff7eb5e58 <PyBufferedRandom_Type>,
[10] = 0x7ff7eb3fe0 <PyFileIO_Type>,
[11] = 0x7ff7eb4968 <_PyBytesIOBuffer_Type>,
[12] = 0x7ff7eb6ee8 <PyStringIO_Type>,
[13] = 0x7ff7eb69f0 <PyTextIOWrapper_Type>
}```
ok now I have a new question... is there any way to get the stub type (_pyio) from the real one?
I realized after some debugging that there is no signature nor any useful info in the bufferediobase one (except maybe the doc, which doesn't even mention the signature)
serms to be the type object with tp_name = tp_name = "symtable entry", yeah
Does Unity support Python ?
Or if it doesn't support Python, then which languages does it support?
there is some possibility with
io.abc._get_dump(io.BufferedIOBase)```
Hey @lusty scroll , wanna help me with my question? I saw in your roles that you're a 'helper'.
i think that topic is out of the range of #internals-and-peps
Yes, I've seen it. Thank you very much again:)
Don't think there's a general way to go to the Python implementation, you have to know the corresponding module name
Hi. I'm newbie. Please take care of me. Thanks !!!
hi, you can ask for help in #python-discussion π
When do differences in speed start to matter?
[sam@samtop]: ~>$ python -m timeit "a, b, c =(1,2,3)"
20000000 loops, best of 5: 16.4 nsec per loop
[sam@samtop]: ~>$ python -m timeit "a, b, c =[1, 2, 3]"
5000000 loops, best of 5: 44.2 nsec per loop
[sam@samtop]: ~>$
When it runs many times
I should have phrased my question better π
π ah okay well what is your question then?
The two statements, do the same thing but the second one unnecessarily creates a list and is 3 times slower
Is that enough for it to be worth doing something about?
Hello
Ah, in Python or your codebase?
In Python
With what Python 3.11 is doing, for sure! They would probably be happy knowing about this
This probably doesn't affect a lot of real code. When are you going to unpack a list/tuple containing only constants?
I'd imagine the extra buffer space allocated at the end to keep appends const time is the main cost IIRC?
this is just some random mypyc knowledge I have fwiw
The tuple version is faster because the whole thing gets compiled into a single co_consts entry in the bytecode, while the list has to be created at runtime
ah
0 RESUME 0
1 2 LOAD_CONST 0 ((1, 2, 3))
4 UNPACK_SEQUENCE 3
8 STORE_NAME 0 (a)
10 STORE_NAME 1 (b)
12 STORE_NAME 2 (c)
14 LOAD_CONST 1 (None)
16 RETURN_VALUE
In [24]: dis.dis("a, b, c = [1, 2, 3]")
0 RESUME 0
1 2 BUILD_LIST 0
4 LOAD_CONST 0 ((1, 2, 3))
6 LIST_EXTEND 1
8 UNPACK_SEQUENCE 3
12 STORE_NAME 0 (a)
14 STORE_NAME 1 (b)
16 STORE_NAME 2 (c)
18 LOAD_CONST 1 (None)
20 RETURN_VALUE
I suppose in this case it could optimize away the list, but I feel like this is very rarely going to apply in real code
oh yeah apparently l = [*(...)] was a performance win, wasn't it lol
This is optimised with a ROT_THREE, ROT_TWO if they're not constant, but I guess it was decided that UNPACK_SEQUENCE is faster than 3 LOAD_CONST? The list should be optimised into a tuple regardless though like other similar usages...
i pretty much hate this answer
- coming from a person who likes to make code fast
anything that is a 1.5x or more speed-up i consider worth it
ok
if probably every programmer had that attitude then everyone would just move to simpler languages and not care at all about C or fortran or any other languages like them
???
people focus on optimizations that will actually improve the speed of your code. this will do practically nothing
when the right code is used above there's a 2.7x speedup and that's even more than the speed-up of this thing https://github.com/python/cpython/pull/30653
ok
largest speed-up is 1.46x faster and is just a 15.7 ns improvement
i'm pretty sure that doesn't do "practically nothing"
The value of any change isn't just how much it speeds things up by, but also how much it costs to implement and maintain.
if it's a change that makes end user's code simpler and faster, removes code from CPython, and is cheap to maintain, that's a no-brainer. If it makes end users' code simpler and faster but it's hard to implement and makes the maintainers of CPython have to do a lot more work, then that may be a different story.
historically, a JIT for CPython has fallen into the latter category, which is why it has never been built - it's always been thought that the cost to build and maintain it would cost too much time and effort from core devs, crippling the development of the language.
they're planning to do that now in python 3.12
so why the change in mentality
3.13 i think they're planning to generate machine code
that's what a JIT is
idk faster-cpython's plan for 3.12 had the word "JIT" in it
There's a more concrete plan about how to minimize its cost and how to maintain it over time, I think. More core developers have stepped up to volunteer for it, and the Python ecosystem has grown to a point where more performance may be a more critical feature than more syntax or built-in libraries. I think it mostly just reflects a shift in priorities
Simple "JIT" compiler for small regions. Compile small regions of specialized code, using a relatively simple, fast compiler.
so a JIT just for some parts in 3.12
Extend regions for compilation. Enhance compiler to generate superior machine code.
then extend the range of the JIT in 3.13?
hm, TIL
Ah - those version numbers are off by 1. What the plan at https://github.com/markshannon/faster-cpython/blob/master/plan.md suggested could be done for 3.10 wasn't started until 3.11, so the JIT that it suggested could make it into 3.11 won't be until 3.12
How do you write a JIT for a language as dynamic as python. I'm not sure I'm really expecting an answer to that haha, it just sounds like a damn difficult task to get good results
Not that I know anything about JITs
uhh pypy might have a thing about it
same question as "how do you write PyPy"
I guess lol
Well, how do you write PyPy?
!pep 654
anyone wanna help me understand this PEP
what don't you understand?
how exception groups work and what's their use compared to normal exceptions
they're a type of exception that holds other exceptions. You can throw a group of exceptions together, which can then be caught and analyzed to see all of the individual exceptions inside the group.
It was accepted a while ago. Will it be in 3.11?
yep
it's already implemented
3.11.0a7 has it
I thought they were going to slow down with actual language changes, but this is pretty substantial. what are the chances that 3.12 will strictly be optimizations and the removal of antiquated stdlib content?
low to 0
there are a lot of core devs, and they have different interests and want to work on different things.
saying that no one can contribute features for a year would just alienate a lot of people.
so, what else is slated for 3.11?
https://docs.python.org/3.11/whatsnew/3.11.html#exceptions-can-be-enriched-with-a-string-note - I've needed this before. That's nice.
# re
Atomic grouping ((?>...)) and possessive quantifiers (*+, ++, ?+, {m,n}+) are now supported in regular expressions. (Contributed by Jeffrey C. Jacobs and Serhiy Storchaka in bpo-433030.)
How can I use this to make my code more esoteric?
how can't you
idk. I just really like regex.
I mean, I suspect any use of those features will make your code more esoteric.
Exception groups aren't too big a change. The new syntax only takes effect with code that's actually raising them, otherwise you can still treat them like any other exception in generic code.
i don't think it can make code more esoteric
it's only a performance boost for when a regex pattern doesn't need backtracking
how can i write a decorator that acts as a transparent replacement of the class it decorates, like functool.wraps
so that
@decorator
class Foo:
...
has the same type etc. as the decorated class?
modify the existing type like dataclasses do?
how does that work?
add methods or whatever to the class you got as the argument and then return it
couldn't i turn it into a proxy?
but the proxy would also have to have the same type.. π
not sure if you can do anything for type() without keeping the obj
Does it really matter? I mean, no-one should have a reference to the original class except for you?
but yeah also that, if it's decorated then only the decorator knows about it
Unless, of course, it is called as other = decorator(original)
well, numpy apparently uses classes as wrappers etc. so quality of the fake is a matter of exception or no exception
how does type(thing) actually work? could i redirect the type lookup?
i'm keeping track of the original thing
so if i could redirect type() at the original thing, that could be a solution
!e ```python
class A:
...
class B(A):
class = A
print(type(B()))
@elder blade :white_check_mark: Your eval job has completed with return code 0.
<class '__main__.B'>
It might be implemented inside of the interpreter to the point where you can't fake it. I think you can fake isinstance() though..
There's no way of redirecting type() other than overwriting it
Yeah it probably does a lookup on the PyObject* struct?
Yes, there's __instancecheck__ and __subclasscheck__
I think you just need to reassign the class inside the __init__ method
I'm on my phone so I'll see how this goes π
Oh wait lmao, right.
!e
class A:
pass
class B(A):
def __init__(self):
self.__class__ = A
print(type(B()))ββ
@west tinsel :white_check_mark: Your eval job has completed with return code 0.
<class '__main__.A'>
Honestly, I need to be reminded not to type code on my phone
>>> foo.__mro__ = bar.__mro__
Traceback (most recent call last):
File "<pyshell>", line 1, in <module>
AttributeError: readonly attribute
sigh
okay, so i need to replace the self.__class__ of the instance? oh my.
maybe i could return a class-factory instead
with __new__ returning the instance of the other class?
insanity π€¦
i think i figured it out
def decorator(thing):
class wrapper(thing.__class__):
def __new__(cls, *args, **kwargs):
print(3434)
return thing(*args, **kwargs)
return wrapper
you can update a class's .__bases__ attribute and that recalculates __mro__
ah, well. it seems to work okay-ish this way
why does POP_JUMP_FORWARD_IF_FALSE and some instances of SWAP become CACHE when i try to create them using builtin code
on 3.11? It's because of Quickening
a new optimization technique that mutates the bytecode in place to use fast paths
the code i have breaks when that happens
is there a way to like disable that?
I doubt it. Bytecode isn't guaranteed to be stable across releases
i'm only testing out the most recent releases here because POP_JUMP_FORWARD_IF_FALSE was just added a few commits ago
well that's why we are in the alpha phase π
.bm madlad builds function object from scratch
also what happened to co_lnotab? the line to byte offset map or whatever
idk it got obliterated and replaced with co_linetable and co_endlinetable
huh, that's so interesting...
so you don't need to put anything there?
i have to for proper line stuff but this is a demonstration and that's not important
HI GUYS
ah i see
anyways to say again could someone see if this is correct https://paste.pythondiscord.com/lafucokivu.py
I get
python3.11 -i py11code.py
Traceback (most recent call last):
File "/data/media/0/src/py11code.py", line 22, in <module>
POP_JUMP_FORWARD_IF_FALSE, 0x10, # 18 (to 52 (SWAP 2)) [iterator, Y]
^^^^^^^^^^^^^^^^^^^^^^^^^
NameError: name 'POP_JUMP_FORWARD_IF_FALSE' is not defined```
Python 3.11.0a6+```
probably a different build
probably.. it's been about a month since I updates / recompiled
it's in python 3.11.0a7
or python 3.11.0a7+
it was just a few commits ago
should i make a bug report for this
like at least try not optimizing co_code when being replaced/made with the code object
ok now I get:
0 RESUME 0
2 LOAD_FAST 0 (iterator)
4 FOR_ITER 26 (to 58)
6 COPY 1
8 LOAD_CONST 1 (63)
10 BINARY_OP 1 (&)
12 CACHE
14 LOAD_CONST 2 (64)
16 BINARY_OP 1 (&)
18 CACHE
20 LOAD_CONST 3 (6)
22 BINARY_OP 16 (<<=)
24 CACHE
26 FOR_ITER 9 (to 46)
28 COPY 1
30 LOAD_CONST 1 (63)
32 BINARY_OP 1 (&)
34 CACHE
36 SWAP 4
38 SWAP 2
40 BINARY_OP 20 (|=)
42 CACHE
44 SWAP 3
>> 46 LOAD_CONST 2 (64)
48 BINARY_OP 1 (&)
50 CACHE
52 SWAP 2
54 POP_TOP
56 RETURN_VALUE
>> 58 LOAD_CONST 0 (None)
60 RETURN_VALUE
Fatal Python error: Segmentation fault
Current thread 0x0000007ff77f2dd0 (most recent call first):
File "<stdin>", line ??? in c
File "/data/media/0/src/py11code.py", line 68 in <module>
zsh: segmentation fault (core dumped) PYTHONFAULTHANDLER=1 PYTHONDEVMODE=1
line 68 is c(iter([7,7,1,1,4])) π
where is co_code ? I'm not seeing it in the struct PyCodeObject ?
I see co_code_adaptive but it's only 1 byte?
theoretically if the CACHEs didn't replace the original instruction it should work properly
>>> from ctypes import *
>>> (c_char * 62).from_address(id(c.__code__) + type(c.__code__).__basicsize__)[18]
b'r'
``` seems to be fine when viewed raw
code is memcpy'd into it
it should actually be 62 bytes long
https://github.com/python/cpython/blob/main/Objects/codeobject.c#L1157-L1175 these specific lines probably caused the CACHE replacing thingy
PyObject *
_PyCode_GetCode(PyCodeObject *co)
{
PyObject *code = PyBytes_FromStringAndSize(NULL, _PyCode_NBYTES(co));
if (code == NULL) {
return NULL;
}
_Py_CODEUNIT *instructions = (_Py_CODEUNIT *)PyBytes_AS_STRING(code);
for (int i = 0; i < Py_SIZE(co); i++) {
_Py_CODEUNIT instruction = _PyCode_CODE(co)[i];
int opcode = _PyOpcode_Deopt[_Py_OPCODE(instruction)];
int caches = _PyOpcode_Caches[opcode];
instructions[i] = _Py_MAKECODEUNIT(opcode, _Py_OPARG(instruction));
while (caches--) {
instructions[++i] = _Py_MAKECODEUNIT(CACHE, 0);
}
}
return code;
}
that replacing thingy affects the interpreter ```py
c(iter([7,7,1,1,4]))
comments are added post-paste
RESUME visited
PUSH_NULL visited
LOAD_NAME visited # c
PUSH_NULL visited
LOAD_NAME visited # iter
BUILD_LIST visited # []
LOAD_CONST visited # (7, 7, 1, 1, 4)
LIST_EXTEND visited # [7, 7, 1, 1, 4]
PRECALL visited
CALL visited # iter([7, 7, 1, 1, 4])
PRECALL visited
CALL visited # c(iter([7, 7, 1, 1, 4]))
inside the c function
RESUME visited # 00 () []
LOAD_FAST visited # 02 (iterator) [iterator]
FOR_ITER visited # 04 (to 58 (LOAD_CONST 0) or X = next(iterator) = 7) [] or [iterator, X]
COPY visited # 06 (copy X) [iterator, X, X]
LOAD_CONST visited # 08 (63) [iterator, X, X, 63]
BINARY_OP visited # 10 ((&) => (Y = X & 63 = 7)) [iterator, X, Y]
missing vital instruction at position 12 (SWAP)
LOAD_CONST visited # 14 (64) [iterator, X, Y, 64]
BINARY_OP visited # 16 ((&) => Y & 64) [iterator, X, Y & 64] #(missing instruction at pos 12 causes wrong stack result but still correct values)
missing vital instruction at position 18 (POP_JUMP_FORWARD_IF_FALSE)
LOAD_CONST visited # 20 (6) [iterator, X, Y & 64, 6] #(missing instruction at pos 18 causes wrong stack result and doesn't branch which is fatal)
BINARY_OP visited # 22 ((<<=) => (Y & 64) <<= 6) [iterator, X, (Y & 64) << 6] #(all goes downhill from here, starting with the wrong value used)
missing vital instruction at position 24 (SWAP)
FOR_ITER visited # 26 (to 50 (RETURN_VALUE) or X = next((Y & 64) << 6) = !!) !! #(missing instruction at pos 24 puts the final nail in the coffin as FOR_ITER tries to tp_iternext an integer)
Windows fatal exception: access violation
Current thread 0x00000670 (most recent call first):
File "<stdin>", line ??? in c
File "<stdin>", line 1 in <module>
@lusty scroll breakdown
ohhh
just to clarify , it crashes for you too ?
I don't know this area well but generally if you construct bytecode manually, you're on your own
Could be worth it to open an issue, and make it clear that you're constructing the bytecode by yourself. If the fix is easy, then it may secretly fix some other bug that could be appearing but not yet noticed.
Any core devs around who would like to review a warnings PR? I've had a patch up to address user feedback (via pytest) for a while, and I'd really like to get it in before the PyCon/beta freeze rush π
https://github.com/python/cpython/pull/91435
I put it in my queue π
relatedly if anyone here knows anything about cookies, I'd love another look at https://github.com/python/cpython/pull/30108. I'll probably merge it but I'm not confident in this area
yes
the function i linked (https://github.com/python/cpython/blob/main/Objects/codeobject.c#L1157-L1175) seems to do nothing in particular but replace a certain number of the opcodes in front of a position with CACHEs https://github.com/python/cpython/blob/main/Include/opcode.h#L219-L231
const uint8_t _PyOpcode_Caches[256] = {
[BINARY_SUBSCR] = 4,
[STORE_SUBSCR] = 1,
[UNPACK_SEQUENCE] = 1,
[STORE_ATTR] = 4,
[LOAD_ATTR] = 4,
[COMPARE_OP] = 2,
[LOAD_GLOBAL] = 5,
[BINARY_OP] = 1,
[LOAD_METHOD] = 10,
[PRECALL] = 1,
[CALL] = 4,
};
found what seems to be the quickener or something https://github.com/python/cpython/blob/main/Python/specialize.c#L253-L269
// Insert adaptive instructions and superinstructions. This cannot fail.
void
_PyCode_Quicken(PyCodeObject *code)
{
_Py_QuickenedCount++;
int previous_opcode = -1;
_Py_CODEUNIT *instructions = _PyCode_CODE(code);
for (int i = 0; i < Py_SIZE(code); i++) {
int opcode = _Py_OPCODE(instructions[i]);
uint8_t adaptive_opcode = _PyOpcode_Adaptive[opcode];
if (adaptive_opcode) {
_Py_SET_OPCODE(instructions[i], adaptive_opcode);
// Make sure the adaptive counter is zero:
assert(instructions[i + 1] == 0);
previous_opcode = -1;
i += _PyOpcode_Caches[opcode];
}
https://paste.pythondiscord.com/itoniwarec.py fixed the problem by adding CACHE 0 after the BINARY_OPs
i had gone through all of this trouble just to optimize dis.parse_varint from py def parse_varint(iterator): b = next(iterator) val = b & 63 while b&64: val <<= 6 b = next(iterator) val |= b&63 return val to py def parse_varint(iterator): val = (b := next(iterator)) & 63 while b & 64: val = (val << 6) | (b := next(iterator))&63 return val to manual bytecode that uses FOR_ITER instead of next() and replaces most of the variable names with stack-only stuff
sounds like you'd be better off using Cython or mypyc
Yeah lol
!e ```py
print >> "a"
@pliant tusk :x: Your eval job has completed with return code 1.
001 | Traceback (most recent call last):
002 | File "<string>", line 1, in <module>
003 | TypeError: unsupported operand type(s) for >>: 'builtin_function_or_method' and 'str'. Did you mean "print(<message>, file=<output_stream>)"?
or is that because of how print worked in python2?
Yes
Is faster-cpython an official thing?
it's a testing ground for stuff that will go into CPython, iiuc
Alright, thanks :D
that's the goal. The degree to which that goal is achievable is yet to be seen, but core devs are actively working on it.
It's a group of developers (sponsored mostly by Microsoft). They use the faster-cpython GH project for ideas and coordination, but their changes are directly on CPython. Everything they've done will be in 3.11.
well, there are ideas they are working on that will have to wait for 3.12, 3.13, etc.
but faster-cpython isn't meant to ever be its own shipped project
I see, that's pretty neat. A JIT would be fun to see
that's really nice, i'm happy to see at least one megacorp giving back!
much better strategy than forking cpython only to abandon it and then kind of restart the fork but not with any specific stated goal or roadmap
(cf. dropbox)
cool new addition to dis ```py
from dis import Bytecode
def quicken(f, times=8):
... for _ in range(times):
... f()
...
co = compile("'a'+(b:='g')","","eval")
quicken(lambda:exec(co,{},{}))
print(Bytecode(co,show_caches=True).dis()) # before
0 RESUME 0
1 2 LOAD_CONST 0 ('a')
4 LOAD_CONST 1 ('g')
6 COPY 1
8 STORE_NAME 0 (b)
10 BINARY_OP 0 (+)
12 CACHE
14 RETURN_VALUE
print(Bytecode(co, show_caches=True, adaptive=True).dis()) # after
0 RESUME_QUICK 0
1 2 LOAD_CONST 0 ('a')
4 LOAD_CONST 1 ('g')
6 COPY 1
8 STORE_NAME 0 (b)
10 BINARY_OP_ADD_UNICODE 0 (+)
12 CACHE
14 RETURN_VALUE
decided to use a version of the function without error checking
sorry for kinda getting lost in all the dis statements, but what are you trying to achieve?
what are the CACHE ops?
they store a cache with data used for specialization
the general idea of the 3.11 specialization is that for code like a + b, the interpreter tracks what the types of a and b are on each execution of the same code. If it gets executed a bunch of times and a and b are both always ints, it mutates the code in place to a new op that is specialized for adding ints. There is a runtime check that makes it fall back to the general, slow path if they happen to not be ints the next time.
(haven't verified they did this specifically for +, but that's the general idea)
Huh, how come it gets faster? 
I mean, you take the runtime hit of recording the types as well as the safety check
ohh interesting
so adaptive=True shows the actual specialized versions?
or is that a different optimization?
PyLong_CheckExact is really fast, just a single pointer comparison. The default version has to do a lot more work
not sure how this is shown in dis, sorry. but what you say makes sense
The cache data is used to store stuff like the index inside a globals dict.
yes it shows co_code_adaptive
code has to be quickened though to actually have changes in co_code_adaptive
that's achieved by the quicken function
https://github.com/python/cpython/blob/dc14e33eff1df31f7c239a8785161db8849cae74/Python/bltinmodule.c#L1916-L1922
where is src for PyNumber_Power
Python/bltinmodule.c lines 1916 to 1922
static PyObject *
builtin_pow_impl(PyObject *module, PyObject *base, PyObject *exp,
PyObject *mod)
/*[clinic end generated code: output=3ca1538221bbf15f input=435dbd48a12efb23]*/
{
return PyNumber_Power(base, exp, mod);
}```
https://github.com/python/cpython/blob/main/Objects/abstract.c#L1150-L1154
https://github.com/python/cpython/blob/main/Objects/abstract.c#L954-L1053
Thanks
hello guys, I started with python and I'm having this problem with pyinstaller, is there any way I can solve this?
do pip show pyinstaller instead, it shows the version + some other stuff
"timeout is too large for a C timeval" on Windows .. what could it be
its coming from some place in stdlib related to sockets
Modules/_multiprocessing/semaphore.c: "timeout is too large");
Modules/selectmodule.c: PyErr_SetString(PyExc_OverflowError, "timeout is too large");
Modules/selectmodule.c: PyErr_SetString(PyExc_OverflowError, "timeout is too large");
Modules/selectmodule.c: PyErr_SetString(PyExc_OverflowError, "timeout is too large");
looks like that exact error isn't from CPython. It may just be giving you the error from the underlying Windows API
ah ```% git grep "timeout doesn't fit"
Modules/socketmodule.c: "timeout doesn't fit into C timeval");
I'll do some digging around there, perhaps it will lead me to a clue
looks like this would happen if your timeout is bigger than 2**32? Not entirely sure
perhaps it's meant to be -1 ?
there's a mailing list thread that mentions it from a few years back https://mail.python.org/pipermail/python-checkins/2015-March/135256.html
i brought it down from 2.62 microseconds (original dis function) to 160 nanoseconds (without GCC __builtin_expect) to 94.9 nanoseconds (with GCC __builtin_expect)
tested this code ```py
try:
print('a')
print('b')
except:
print('oh no')
Hi folks! Does anyone know how to determine when a module was added to the Python standard library? I'm trying to develop a timeline of several modules, but I'm not sure how to date things.
you can go on the cpython repo and see the oldest commit of a module
for example the dis module and some others: https://github.com/python/cpython/commit/217a5fa3c33ae58c1fe420f94eeb7e806961c3c1
Okay, at least that's something! I thought surely there must be a since or introduced label that I was just missing. Perhaps not.
Recently added modules have it in the docs, e.g. https://docs.python.org/3.10/library/statistics.html says "New in version 3.4"
where "recently" is "during the lifetime of Python 3"
I'm specifically wondering about the select module. I'm researching the topic of asynchronous I/O support in various programming languages. It seems that select has been around for quite a while!
So, 1.0 it seems.
I'm shocked. Source versioning has been maintained from the outset? Wasn't Python just a bag of code being archived and shared? Was CVS used at the time?
Well done, Guido!
github actually has a blame feature that allows you to view the oldest commits of a file
so that makes it easier i guess
...til git grep exists
Is this correct, that Callable was removed from collections module in 3.9?
https://github.com/tensorflow/tensorboard/issues/3202
I posted a PR to pyreadline assuming it was just that way because I was using 3.11. could it be it's been broken since 3.9
you're supposed to import it from collections.abc instead yeah
ahh. wow ..
nobody on Windows must actually be using the real repl directly :p

like instead people are using IPython or vscode or such things
just guessing as to why nobody would have noticed all this time
how to implement callable(x) in python?
def callable(x: object) -> TypeGuard[Callable[..., Any]]:
return any('__call__' in cls.__dict__ for cls in type(x).__mro__)
is this correct?
hasattr(x, '__call__') and hasattr(type(x), '__call__') isnt correct
afaik you could techically make a callable type in C that sets tp_call without setting __call__
its weird π€
pretty sure the type creation machinery would automatically create __call__ (unless you do something really crazy)
ah that makes sense. although if thats something you'd call really crazy you should see some of the stuff i put in #esoteric-python
π
def callable(x):
try:
x()
return True
except TypeError as e:
return "is not callable" not in e.args[0]
except Exception:
return True
:P
you should check this out #esoteric-python message
def g():
raise TypeError('is not callable')
callable(g) # False
Could inspect the traceback to see if the error comes from within the function of from callable
there's a built-in callable, you can use that
!e
print(callable(int))
print(callable(42))
@grave jolt :white_check_mark: Your eval job has completed with return code 0.
001 | True
002 | False
that's "jump in before you look"
i'm doing ```py
def is_callable(thing):
try:
object.getattribute(thing, "call")
return True
except AttributeError:
return False
which works quite well
haven't come across any callable that didn't work with that so far
class NotCallable:
__call__ = 23
>>> is_callable(NotCallable)
True
Yes, but it isn't
you need to look at the type, not the instance
!e
class NotCallable:
__call__ = 23
NotCallable()()
@quick snow :x: Your eval job has completed with return code 1.
001 | Traceback (most recent call last):
002 | File "<string>", line 3, in <module>
003 | TypeError: 'int' object is not callable
>>> NotCallable()
<__main__.NotCallable object at 0x0000021C8B5CD850>
but it is π
The class is, the instance isn't
!e
def is_callable(thing):
try:
object.__getattribute__(thing, "__call__")
return True
except AttributeError:
return False
class NotCallable:
__call__ = 23
not_callable = NotCallable()
print("callable?", is_callable(not_callable))
not_callable()
@quick snow :x: Your eval job has completed with return code 1.
001 | callable? True
002 | Traceback (most recent call last):
003 | File "<string>", line 13, in <module>
004 | TypeError: 'int' object is not callable
to be fair, builtins.callable() also believes it is callable
okay, this is interesting
def is_callable(thing):
try:
return callable(object.__getattribute__(thing, "__call__"))
except AttributeError:
return False
it actually can smell your int stink
but it's wrong-ish
>>> is_callable(NotCallable())
False
>>> is_callable(NotCallable)
False
>>> NotCallable()
<__main__.NotCallable object at 0x0000023E33FFF250>
>>> class Foo:
pass
>>> is_callable(Foo)
True
def is_callable(thing):
try:
return callable(object.__getattribute__(type(thing), "__call__"))
except AttributeError:
return False
hm why the __getattribute__? why are you bypassing descriptors?
descriptors.. are weird
as the docs for callable say, not callable(f) implies that f() will fail, but callable(f) doesn't mean f() will work
and you can't really do better than that
i built my check for mass-decoration
and properties are tricky to decorate
Not really sure what your function adds over builtins.callable
Yeah, if I just do getattr(type(thing), "__call__") it still seems to work the same, except it catches descriptors bound to the name __call__ :D
builtins.callable believes my NotCallable() instance above is callable, this function doesn't
well, for one, the modified check now detects weird non-callable __call__ madness
It only does one indirection, though. If __call__ is an object that itself has a __call__ attribute (on the class) that's an int or something, it doesn't notice it
i've stumbled over a number of cases where callable didn't work properly
it always works properly, it's just that it's impossible to check whether calling an object will succeed without actually calling it
while x is not x:x=getattr(x,"__call__")
Yes. Curious to hear of non-contrived cases where callable() doesn't do the job
You can also check the tp_call slot pointer a la C
(I think that's what callable does?)
yes, that's what it does
it's been a while since i worked on that check, i think i built it also to find properties (which worked, but i changed the mechanics now)
and callable() doesn't work on properties
what do you mean by "work"?
it can't tell if something is a property or not
how does that relate to whether it's callable
as i said, i built the check to find callables for mass-decoration. the very first application was indeed to decorate properties
and properties are call-by-implication so to say
which reminds me, i need to put property-decoration back in there
properties and other descriptors are invoked with __get__ so I don't see how it would be feasible to detect whether the method calls something internally
there are some inspect helpers for finding descriptors
and where does callable come in play here, just as a bonus?
you mean how it fails?
or what i'm doing with it?
confused :p
fascinating. callable is qualitatively much less strict than the modified is_callable check. using my check, only 1455 things are decoratored in numpy, with callable as check it's 2777 things
i'll have to look into that some other time
what about callable(object.__getattribute__(thing, "__call__"))?
your code fails in this case: ```py
is_callable(...)
True
...()
<stdin>:1: SyntaxWarning: 'ellipsis' object is not callable; perhaps you missed a comma?
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'ellipsis' object is not callable
>>> class X:
... __call__ = 123
...
>>> class Y:
... __call__ = None
...
>>> callable(X())
True
>>> callable(Y())
True
>>> class Z:
... ...
...
>>> callable(Z())
False
come on, stop messing around with callable
it's pretty limited
just accept you'll have to special case every type of callable you're interested in
!e
def really_callable(thing):
try:
thing.__get__
thing.__call__
return True
except Exception:
return False
print(really_callable(...))
print(really_callable(lambda i: None))
print(really_callable(really_callable))```
@lusty scroll :white_check_mark: Your eval job has completed with return code 0.
001 | False
002 | True
003 | True
@verbal escarp
can't you just use hasattr instead of using a try catch
smh why is that a bare except
Wait wut str.istitle vs float.is_integer
>>> really_callable(numpy.zeros)
False
>>> numpy.zeros((2,2))
array([[0., 0.],
[0., 0.]])
false negative
hm
how?
!e import numpy ; print(type(numpy.zeros))
@white nexus :white_check_mark: Your eval job has completed with return code 0.
<class 'builtin_function_or_method'>
c-extension
welp
it discovered a bug with inspect.getsourcefile
or no wait
nope that's with my code
iter() and probably aiter() relies on callable
thanks, just solved a bug in my code
what..?
long story short is i wasn't properly catching c-extensions
oh
!e
args = map(int, (10e10, 10e20, 10e30))
print(pow(*args))
print(pow(*args))
@quick night :x: Your eval job has completed with return code 1.
001 | 7063394790938602034297680429056
002 | Traceback (most recent call last):
003 | File "<string>", line 5, in <module>
004 | TypeError: pow() missing required argument 'base' (pos 1)
why is this happening
this doesn't happen without the map, what is map doing here?
!e
args = (int(10e10), int(10e20), int(10e30))
print(pow(*args))
print(pow(*args))
@quick night :white_check_mark: Your eval job has completed with return code 0.
001 | 7063394790938602034297680429056
002 | 7063394790938602034297680429056
!e
args = map(int, (10e10, 10e20, 10e30))
for arg in args:
print(arg)
for arg in args:
print(arg)
@quick night :white_check_mark: Your eval job has completed with return code 0.
001 | 100000000000
002 | 1000000000000000000000
003 | 9999999999999999635896294965248
huh, map can be iterated over only once?
yes
why
generally speaking an iterator will work just once
when you loop over a list twice it creates two separate iterators for each
an iterator only needs to have a __next__ method, which should either provide the next value to be taken up by the loop variable, or raise StopIteration to signal that the loop should end
it doesn't have to support a mechanism for "resetting"
ah ok, i didnt know it creates two iterators in case of lists( and others alike)
thanks π
It is also false negative on builtin-function-or-methods, because they dont support descriptor protocol
ah, it is mentioned here
That's an interesting statement. The current protocol declared for an iterator is that it also needs to have an __iter__ method that returns the iterator itself, although that is not enforced in CPython. Some features do rely on it and not adding such an __iter__ method would break quite a few things, including:
a, *b, c = some_iterable_with_an_iterator_without_iter_method
It would also mean that something like this is not guaranteed to work for all iterators:
for element in iterator:
...
yeah. i remember there was an github issue on this
oop you're in the thread too
i have no idea
but any exception something would throw in there
I guarantee you don't want
fixed @white nexus π
now Ctrl+C and sys.exit() won' fail :p
https://github.com/python/cpython/pull/32242#issuecomment-1106447828 Does anyone have any ideas?
I'm quite lost π
At the surface, the CI tests pass. But it fails when compiled with optimisations?
I can at least reproduce it. The failure mode suggests something is wrong with the context's precision, right?
huh it still fails when I revert your commit
Is this even when recompiling python?
Looks like it, although I'm not sure
It's weird because it seems to just be one test
yes
weirdly this passes ./python.exe -m test test.test_decimal -m '*test_from_tuple*'
but this fails ./python.exe -m unittest test.test_decimal.CWhitebox.test_from_tuple
doing some printf debugging scan_dpoint_exp in the C code is behaving differently
no it's not, I printed the address instead of the value
the value of prec in the global context is different
https://github.com/python/cpython/pull/91825 should fix it though I still don't understand exactly what happens
Oh, weird
Is the yield keyword in Python the same as a yield system call on an OS?
Same as in yield invokes that system call? If so, no
Ah gotcha. I am taking a systems class in C and saw the similar keyword
The yield keyword in python sort of "pauses" a function in place, returning from it, and when you call that function again, it resumes execution at the point where you used yield
What's the command for serving documentation?
I normally use php -S or open the html files with my browser. I remember seeing a tool being discussed somewhere but forgot what it was
pydoc -b can do that (might need to be pydoc3 -b)
I didn't know about this thank you
It doesn't do exactly what I wanted it to do but I can see myself using it in the future
I'm talking about the rst files in the Doc folder
I usually just do make html in the Doc folder and open the generated files in my browser
the pydoc thing had a security vulnerability recently
Oh, is there a link to information about that?
Thanks!
Hmm, for the new forward class and continue class statements does anyone know any other languages that has something similar?
what are you referring to?
Seems convoluted if the purpose if purely for typing. I agree with the premise and it pisses me the hell off that I cannot do circular references but at the same time, I don't know if I would go through the trouble
dont like it
what issue does it solve that automatically stringifying annotations won't
Like I've totally been in a situation (specifically when making pathlib.Path subclasses that had properties referring to parent/child object directly. I couldn't type annotate it, and it bothers the hell out of me, but this is just like so much work.
The issue is, a lot of libraries are popping up that use annotations at run time, and stringifying doesn't work for them
they can use get_type_hints then to evaluate the annotations
I think you would still hit the same issue, you could be trying to evaluate it and get a class that doesn't exist yet
Like I have no idea at what stage in class creation say pydantic needs the run time type of the type annotation
It could be in at the end of __init__ after its already created or even in __call__
No promise that the type being annotated will exist
I still don't understand what the problem with the pep titled deferred type annotations using descriptors or something like that. An alternative to the stringification of type hints pep from Lukas
So, there are merge conflicts
But even when resolving and removing the markers, I can't mark it as resolved
C/C++
class X; # forward declaration
class Y {
X x; # use X before implementation
};
class X {
# actual implementation
Y y;
}
also Pascal/Delphi uses forward references in unit(=module) similar to C/C++ headers:
unit uSomeUnit;
interface
// declaration
implementation
// implementation
can you link the PR? I can give it a try
@feral island https://github.com/python/cpython/pull/32339
One thing to note is that versionchanged applies to the function above
And was introduced in a different commit
It tripped me up, I thought someone added it to set_start_method
I get the same thing π¦
maybe fix it on the command line instead (git fetch upstream; git merge upstream/main, depending on your remote name)
wait I did it
removed the "disabled" class from the button in the browser inspector π
π
Ah, cool
C++ does allow this, but at the point where the class has been declared but not yet defined you can only declare pointers or references to instances of it, not variables of its type. So in your example, you could do py class Y { X *x; }; but not ```py
class Y {
X x;
};
I think I like this proto PEP. It seems pretty easy to implement, it seems reasonable easy to explain how it works, and it seems like it would thoroughly solve the problem
And it certainly helps that it's analogous to how declarations work in C and C++, so lots of developers would already have a frame of reference for understanding it
Whats a PEP
!pep 1
PEPs are formal proposals for changes to the Python language. If you click the Link in the embed above, it explains PEPs in detail
in general yes but not for that specific C++ example because of mutually recursive fields
pointers to incomplete types are fine because their size is known
(or, er, it might not just be the mutual recursion since declarations should be properly ordered and so the size of X can't be known at Y's declaration site)
Sorry to bug you about it, but I think this is ready to merge and would love to get it done before PyCon π
I'll do it today, I'm a bit hesitant to merge a new feature in a module I'm not familiar with it in case Raymond Hettinger comes out of the woodworks and decides it's a terrible idea
Haha, fair enough! If that does happen I'll accept full responsibility (and get you dinner sometime)
I've had my own experience with ideas-I-thought-were-good meeting Guido and Raymond π
!unsilence
β unsilenced current channel.
π
Co-authored by Alexey Boriskin
https://bugs.python.org/issue15795
TODO
Documentation
Document constants
Document permission preservation parameters
Testing
Get current tests to pass
W...
Is there a method to get the default mode of created files?
My idea is creating a file and checking what mode it has
You mean the umask?
import os
umask = os.umask(0o777)
os.umask(umask)
For some reason you need to set the umask in order to get its current value. Note that the umask is what's forbidden, so it needs to be xored with 0o777 to get the default permissions, I think.
Converting to "int" failed for parameter "pep_number".
!pep <pep_number>
Can also use: get_pep, p
Fetches information about a PEP and sends it to the channel.
Exactly what I want, thank you!