#internals-and-peps
1 messages · Page 49 of 1
i just wanted the same "thing" to be printing output to the console... I can probably just make one in each module tho
there's no need to touch any other module's logger in order to implement --debug
Testing log output feels... A bit overkill. I dunno, I never do that personally.
transitioning away from that model made it easier to config. propagating and <something else i forget> was no longer required.
@magic python Do that and the log output will be more useful, since the log entry will indicate where the log entry was created.
who tests the tests 😱
Usually not the output, but that something was actually logged
@raven ridge i don't want debug output from third-party modules like gensim, so i need to restrict it at least to my own top level module
but yes that's fair
if it's all at one top level
@gloomy rain yeah - I just tried it and it seems so, idk why i didn't just do that straight away 🤦♂️
e.g. main_logger = logging.getLogger('top_level_package')
@paper echo Pretty sure you can just configure that.
is there an accepted workflow to this ? Seems something that a few do differently (going by this thread at least)
propagating and <something else i forget> was no longer required.
disable_existing_loggerswas the something else.
Without having to touch the code itself.
@magic python Haven't seen any good arguments against @raven ridge's approach so far.
They usually know what they're talking about anyway.
@raven ridge anything that i could google to get an outline of what you're saying?
or is
log = logging.getLogger(__name__)
about it
@cinder parrot This is not a help channel. Please see #❓|how-to-get-help
oh ok
that's pretty much all. inside of whatever module you're using as your main, that will create a logger called __main__. In every other module, it'll create a logger called foo.bar.baz or whatever. Each of them will propagate log records up to the root logger by default. In your if __name__ == '__main__': block you confiugre the root logger (get it using logging.getLogger()). If you need to get a handle to another module's logger for some reason (like to reduce verbosity for just one module), logging.getLogger('foo.bar.baz') lets you do it. Because of how propagation works, that also lets you cut down the verbosity for an entire package at once, isntead of just one module, by configuring logging.getLogger('foo.bar') instead.
@raven ridge hrm ok , thanks. So using a logger for each file wouldn't be ok?
https://www.youtube.com/watch?v=Pbz1fo7KlGg is a good talk about the logging module, if you want to learn more about how to use it properly.
@raven ridge hrm ok , thanks. So using a logger for each file wouldn't be ok?
@magic python I think you misunderstood me - using a distinct logger for each file, named by its module name, is the norm.
oh ok 😅 all good
__name__ <- This will have a different value in every file.
sounds like i should watch that talk 🙃 thanks for the link
using getLogger(__name__) will insert the filename into the log entry.
unless your config changes the format...
So if you do logger = logging.getLogger(__name__) in each file, you will get different loggers.
yeah, in every file except the initial one, __name__ resolves to the module's dotted import path. in the main it resolves to "__main__" instead. All of those will propagate log records to the root logger by default.
logging is an awesome lib. its modularity comes with the cost of complexity though. 😄
no one uses loguru then?
@void sonnet Fortunately, most of the complexity is contained in the configuration. It's very easy to use "in the field".
fair point. i dove into it deeper needing to use SystemLogger. 😵
does singleton have anything to do with why one can't deepcopy a matplotlib axis object btw?
or is that completely unrelated
I've also just realised, if you have a separate logger in each file - it's pretty hard to change the level for logging isn't it?
no - you change it at one level of the hierarchy, and everything below it inherits that level by default.
you can change it in each file.
logger = logging.getLogger(__name__)
logger.setLevel(level)
you can set handlers, filters, etc per file.
@magic python If you use loggers with dot-separated names (like module names), it creates an implicit "logger tree" that you can configure in a cascading manner.
So if you set log level DEBUG for "foo", then you also implicitly set it for "foo.bar" and "foo.baz"
But you could also explicitly override it for a specific subtree
the talk I linked to above would probably be very helpful for you, @magic python 🙂
btw this is a great argument for using __main__.py
since if you -m your top level package
and naively use getLogger(__name__)
it won't be the parent of any of your sub-package loggers
i still like having the option/control to configure individual loggers, but you're probably right that getLogger vs importing isn't any worse, and enforces better habits
those individual loggers already have to be registered with logging by name in order for them to do any useful work, so there's no reason not to look them up in that registry, via logging.getLogger, rather than trying to import them. That way there's no ordering dependency; you can do that even before that module has been imported.
idk how this works with loguru... guess i'll go back to default ha
Is there a way to make logging print out the module it's called from? If i use:
logging.basicConfig(
format=f"module : {__name__.split('.')[-1]} %(asctime)s %(message)s",
datefmt="%m/%d/%Y %I:%M:%S %p",
level=logging.INFO,
)
in the first call then __name__ will be the file of the 'main' module rather than the submodules
@magic python use %(module)s: https://docs.python.org/3/library/logging.html#logrecord-attributes
the module it's called from is __main__. The name of the first module in a Python program is always __main__.
@raven ridge rie means a later logging call from another module would still say __main__
ctypes does not raises OSError when the function calls returns non zero codes?
why would it? What if the function is sin or strlen or printf or something like that?
yo can smone pls halp me i cannot figure out how I make my 3D mincecraft geme
https://docs.python.org/3/library/ctypes.html#loading-dynamic-link-libraries
The error code is used to automatically raise an OSError exception when the function call fails.
that's specific to oledll, not for everything you could call with ctypes.
Why doesn't python support bare tries?
try:
CODE HERE
#no except following```
being equal to
try:
CODE HERE
except:
pass```
Explicit is better than implicit?
Along with the fact that it'd be discouraged in 99% of its uses
Oh, that's true too.
because that's also equivalent to just not having the try: at all - so why allow it? There's already a clear and obvious way to run some code without catching exceptions or running finalizers, and it's not having a try at all.
In that case, why is a bare except allowed?
oh right, using a with suppress():
except: pass will swallow all exceptions, different than no try at all
ah, I misread.
yeah, suppress in that case being the obvious way to do that.
In that case, why is a bare except allowed?
that's actually a good question, though - that footgun probably should be removed from the language...
it's so nearly always the wrong thing to do that linters warn about it - forcing you to explicitly use BaseException when that's what you mean would be an improvement.
can you except like
in hindsight, it might have been better to disallow bare except, but it would be hard to remove now
hold on
rip
you can inherit from baseexception which except doesn't catch by default afaik
or something
there's some trickery with that
@peak spoke If explicit is better than implicit, why not use isinstance on an expression name?
try:
...
except e:
if isinstance(e, TypeOne):
...
elif isinstance(e, TypeTwo):
...
raise
Not sure how that fits here
@rich wharf that's just more verbose than using except clauses
alright
too bad bare except didn't get removed from the language in 3. We're not likely to get another chance on that one for a while.
Any idea what the initial reason for it was? Not many cases where you need BaseException instead of Exception
I'm guessing "anything" just seemed like a sane default once upon a time, and no one caught it until it was firmly ingrained in the language.
Lists and arrays are different in python yes. Primarily difference being, lists are heterogeneous, they can contain items with different data types.
Arrays are understood to be homogeneous.
In python, usually when you say arrays, it's convention to be interpreted as numpy arrays
Sounds like a made up word.
I have that in c# and Java atleast
Sounds like not python. :)
Tell us what they are in c# and java, and we can probably find you the equivalent term in python if there is one.
Yep, thought there would be something similar in oython
They may or may not exist. Cause between arrays and lists I honestly don't see the need for another array like or list like container
So arraylists are just more functionality with arrays, you can add and pop anywhere you want, unlike arrays in Java, which are just declared and don't have much changability, tho you only have, 1D arraylists
My guess is arraylists are lists in python
Sounds like it, aye
Is the add and pop at the end an O(1) operation in arraylists?
If yes, they are similar to python lists in that sense. However, are they also heterogeneous or homogeneous
I didn't really understood what u said, but I know for a fact that they are homogeneous
Oh ok
Meaning only one type of value
Uh, that notation talks about time complexity of operations
It's basically like a measure of how much slower code gets as you increase the data points (or n) for any task
Uhh k
Time complexity is a pretty useful topic to pick up, I'd highly recommend looking into it in more detail when you have time
Pun intended 😉
K
The closest analogue to Java's arraylist is Python's list. Both are internally implemented as dynamic arrays.
And Javascript's arrays are actually ArrayLists, and that brings some confusion as arrays can be heterogenous.
But most of the time, I think, lists are used homogenously as well
well, they are still homogenous. All JS objects share a common type, as do Python objects.
Well, technically yes.
But semantically, python objects can still have different types
as well as JS objects
you can also do ArrayList<Object> in java, which is in essence what python/JS lists are
but honestly, homogenous vs heterogenous is more of an implementation detail than a useful property, because same types as per the language can be entirely different types semantically. For example a string array where each even position is a name and each odd position is an email address. Both are strings, yet are disjoint types
@hollow crane You wrote pront (''hallo''), you should fix that.
Oh, I am sorry I didn't see that.
Does threading.Thread maps to a OS thread?
no
wait sorry
miss read that
Yes, threading or any python threads (That i know of) are OS threads in implementation
:incoming_envelope: :ok_hand: applied mute to @subtle pike until 2020-06-18 12:38 (9 minutes and 59 seconds) (reason: chars rule: sent 3005 characters in 5s).
!unmute 248887037447503872
:incoming_envelope: :ok_hand: pardoned infraction mute for @subtle pike.
can you override the assignment operator in python?
@tacit hawk the threading library uses the more lower level _thread library under the hood (this module was optional up to 3.6.x). I do not believe the definition of a thread is specified, it appears to be left to the implementation - provided it implements the threading and _thread interface. In the implementation of threading.Thead.start (https://github.com/python/cpython/blob/3.8/Lib/threading.py#L852) an ivokation to _start_new_thread(self._bootstrap, ()) is made (this is aliased to _thread.start_new_thread. The implementation of _thread is more nuanced, the CPython source will use one of two implementations depending on if _POSIX_THREADS is defined upon compilation or not. If _POSIX_THREADS is not defined pthread.h will be included (https://github.com/python/cpython/blob/3.8/Python/thread.c#L11).
#ifndef _POSIX_THREADS
/* This means pthreads are not implemented in libc headers, hence the macro
not present in unistd.h. But they still can be implemented as an external
library (e.g. gnu pth in pthread emulation) */
# ifdef HAVE_PTHREAD_H
# include <pthread.h> /* _POSIX_THREADS */
# endif
#endif
Additionally, the CPython implementation will opt to use the pthread implementation, otherwise it will assume the NT implementation (https://github.com/python/cpython/blob/3.8/Python/thread.c#L80).
#if defined(_POSIX_THREADS)
# define PYTHREAD_NAME "pthread"
# include "thread_pthread.h"
#elif defined(NT_THREADS)
# define PYTHREAD_NAME "nt"
# include "thread_nt.h"
#else
# error "Require native threads. See https://bugs.python.org/issue31370"
#endif
Delving into thread_pthread.h, PyThread_start_new_thread (https://github.com/python/cpython/blob/3.8/Python/thread_pthread.h#L236) makes calls to pthread (https://github.com/python/cpython/blob/3.8/Python/thread_pthread.h#L280)
ooh
status = pthread_create(&th,
#if defined(THREAD_STACK_SIZE) || defined(PTHREAD_SYSTEM_SCHED_SUPPORTED)
&attrs,
#else
(pthread_attr_t*)NULL,
#endif
pythread_wrapper, callback);
(obviously). pthread makes userspace threads (not individually kernel backed). Looking into the NT implementation, we find the following invokation in PyThread_start_new_thread (https://github.com/python/cpython/blob/3.8/Python/thread_nt.h#L180), ```c
hThread = (HANDLE)_beginthreadex(0,
Py_SAFE_DOWNCAST(stacksize, Py_ssize_t, unsigned int),
bootstrap, obj,
0, &threadID);
The documentation for `_beginthreadex` is found here: <https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/beginthread-beginthreadex?view=vs-2019>. These are also userland threads (not kernel backed). So, yes, CPython will use userland threads but the specification does not specifically require this. Alternative implementations are free to implement green threads or any m:n threading model they choose.
hope this is enough info to satisfy you 😄
@red solar not without changing the syntax itself or some encoding magic afaik, but for classey you can change setattr/setattribute etc.
but i can't override x = y, only attributes of the class?
hmm is there __get__ and __set__?
yeah, cannot override that (except in class definitions to an extent afaik)
assignment is a simple statement, so you can't do that without being in some of the more magic classes
what would be one of the more magical classes?
and there isn't that much modifying it allows you to do
I haven't done it myself and can't look now but things like Enums modify it in some way to get subclasses on the values you assign it
In [6]: class A(Enum):
...: a = 5
...:
In [7]: A.a
Out[7]: <A.a: 5>
Ah, yeah, descriptors
What do you need?
well i don't need it
get and set is used for the descriptor protocol, but you can't modify the assignment statement iself globally or at the module top level
lets say i have class A with attribute a, have and instance i, i = 3 sets a attribute to 3
rather than assigning a new object to that identifier
In the case of Enum, I'm not sure what does the magic. It's probably done after the class is defined by a metaclass.
Let's test taht
!e
import enum
class Foo(enum.Enum):
a = 1
print(a)
print(Foo.a)
@wide shuttle :white_check_mark: Your eval job has completed with return code 0.
001 | 1
002 | Foo.a
Right, so it happens after, maybe even at access time
hmm, I have not looked into this yet
lets say i have
class Awith attributea, have and instancei,i = 3setsaattribute to 3
@red solar
Is there any reason for not assign i.a = 3? That would make things much easier.
just nicer interface
do you mean something like
class A:
n = 0
def __str__(self):
return "A"
i = A()
i = 3
print(i, A.n) # A 3
wanna implement atomics, and would be nice if you could use them as normal numbers too
without needing to do .val the whole time
the other operators seem simple
but assignment not so much
oh wait that's a question for me - uhh
sure yeah
that
sry got distracted lol
I think that entirely impossible. i = 3 does not ever actually look at value of i, so you would need to somehow recompile everything to do that, because you cannot determine at runtime whether this should happen or not
ah 😦
I wish static methods were indistinguishable from classmethods in python
so you could have this sorta stuff:
class Q:
pass
t = Q()
t.a = "FOOBAR"
def func(self):
print(self.a)
t.call = func
t.call() ## FOOBAR
I know you can do it with .bindstaticmethod
The problem here is that you're assigning the function object to the instance instead of the class
the function objects we use for methods are assigned to class attributes
and that's when the descriptor protocol kicks in to insert the instance you access the class attributes on
If you were to assign func to Q.call, it would work
Oh, interesting
Also mind that a class method would be bound to the class object, not an instance of it
methods are just regular function objects (unless you decorate them with something that returns something else)
!e
class Q:
pass
t = Q()
t.a = "FOOBAR"
def func(self):
print(self.a)
Q.call = func
t.call() ## FOOBAR
@wide shuttle :white_check_mark: Your eval job has completed with return code 0.
FOOBAR
But does calling a method in python still invoke __getattr__?
or is that only for fields
lemme check
it doesn't!
Python 2 had unbound methods, but 3 only has functions for functions/ static methods, and then bound methods to the class/instance depending on hwo you defined it
@tawdry gulch Now try __getattribute__
Oh, sh*t I used the wrong dunder
No, they both exist
It has to trigger one of the methods for getting attributes or it wouldn't be able to fetch the object
But they play a different role in the attribute access logic
right
__getattr__ only gets called if __getattribute__ raises an AttributeError (or calls __getattr__ directly)
why tho - why have both
In this case, the attribute exists, so __getattribute__ will already return it
It's to provide a safe way of dynamic attribute handling
Okay, I clearly need to be less cocky and look up what these other dunders do! : P
The __getattribute__ takes care of explicitly defined attributes first
right
Leaving you to safely implement __getattr__ to handle "dynamic"/"virtual" attributes
i think i have some random spicy examples somewhere
We all know you do
You're spice-die now
Unless you've got a salty example instead
ah, so something like this, without screwing up things like __init__
class NS:
def __init__(self, **kwargs):
self._dict = some_dict
def __getattr__(self, elem):
try:
return self._dict[elem]
except KeyError:
raise AttributeError
```or maybe just keep the keyerror
I think people expect an AttributeError when accessing something as an attribute
def __getattr__(self, name):
if name.startswith('bind_to_'):
return self.__class__.__dict__[name[8:]].bind_deco(self)
return super().__getattribute__(name)
i had this in a long class that allows you to bind functions to attributes
why not use
vars(type(self))
``` and `getattr`
cause i like chaining better
No offence but that looks quite fragile salt
metaprogramming is fragile
fragile?
but convenient and people just have to not be stupid
Just the meddling with the strings, man I could literally trip myself up with that so easily haha
it's just extra functionality
we used it in a our peekable class too
its really interesting though
Please tell me you called it peek-a-boo
def __getattr__(self, attr):
"""Return the nth item in peeked with n _'s, e.g., `.peek(2)` equivalent to `.__`"""
n = attr.count('_')
if n != len(attr):
return super().__getattribute__(attr)
return self.peek(n)[-1]
In [37]: p = peekable(range(100))
In [38]: p.__
Out[38]: 1
In [39]: p._____
Out[39]: 4
this was just silly though
Lol
@deft pagoda That's... I don't know yet how that makes me feel
I mean, it's unusable in actual code, unless you've got magic eyesight for reading the number of _ when the number gets big
but it's quite funny as well
Hey, get me p.______________________________________________
well it was mostly for like 1 to like 3
p._{n}
which i suppose you could just add manually as properties
p._10
it is hilarious
I found my code from a year ago and it just doesn’t have blank lines lol
use this cool salt feature to get some vertical rulers as separators
lol
Is there a font that separates underscores from each other to help count them?
there is likely
@wide shuttle I once wrote the code that rotates all contents of the file 90° clockwise 
hey guys. Im trying to capture the stack trace for the given line, which doesn't throw any error. Ive been looking at inspect, pbd and traceback, but didn't manage to wrap my head around it. Could anyone give me a hand pls?
that's for a different channel
okay, sorry for the off
that would be an interesting code golf. A program which given a filepath would print that file rotated 90°
yeah my code was a mess during the time I wrote it so time for a rewrite
@deft pagoda which one would be the correct channel?
@flat gazelle oh I probably know a way to do that
#❓|how-to-get-help will fix you up
I would say yes
alright that makes it much simpler
from itertools import zip_longest
def rotate_text(text: str) -> str:
return "\n".join("".join(chars)[::-1] for chars in zip_longest(*text.splitlines(), fillvalue=" "))
def rotate_file(path: str) -> None:
with open(path, "r") as file:
data = rotate_text(file.read())
with open(path, "w") as file:
file.write(data)
def rotate_print(path: str) -> None:
with open(path, "r") as file:
print(rotate_text(file.read()))
path = input("Enter path: ")
rotate_print(path)``` @flat gazelle alright my brain came up with this
not a golf but it works
with open(input())as f:a=[x.rstrip() for x in f];[print(''.join(a[r][c:c+1]or' 'for r in range(len(a))))for c in range(max(map(len,a)))]
``` attempt one
src/py_nitro_emulator.cpp:9:5: error: ‘nitro_emulator’ does not name a type
9 | nitro_emulator* emulator;
| ^~~~~~~~~~~~~~
src/py_nitro_emulator.cpp: In function ‘int NitroEmulator_Init(PyObject_NitroEmulator*, PyObject*, PyObject*)’:
src/py_nitro_emulator.cpp:13:11: error: ‘struct PyObject_NitroEmulator’ has no member named ‘emulator’
13 | self->emulator = new nitro_emulator();
| ^~~~~~~~
src/py_nitro_emulator.cpp:13:26: error: expected type-specifier before ‘nitro_emulator’
13 | self->emulator = new nitro_emulator();
| ^~~~~~~~~~~~~~
error: command 'gcc' failed with exit status 1
This code works fine if I use it directly in src/py_module.cpp, but when compiling it on the side and including it with a header it fails suddenly. How would I fix this?
print((lambda p:"\n".join("".join(c)[::-1]for c in __import__("itertools").zip_longest(*map(str.rstrip,open(p)),fillvalue=" ")))(input()))``` and golfed
you dont save any chars by defining n as \n, still takes the same amount of chars
actually
you waste one char because of the comma
also, open(p).readlines() exists
that would keep newlines, which mess it up
doesn’t it yield a generator
readlines is a list
ah okay
the file itself is a generator
and yeah
I should probably also use .read().split('\n')
though [*map(str.rstrip,f)] is just as long
seems so
I remember when a year ago I golfed something that would be incredibly short but needed ~40TB of RAM to handle the created data
you don’t need brackets though?
for what I use, I do. I use indicies rather than zip transposition
I did, but I have no idea
hm
As my Rustacean friend says, CPP being CPP 
got it to 118 chars.
you could probably get it down even further using VTE codes 
How do i analyse images. For example do like:
Check the image resolution
Check how many red pixels exist
Check the position of blue pixels
Does anyone know how to help
#python-discussion is fine, but if you want dedicated help, check out #❓|how-to-get-help
What is the point of enums? Is strictly to catch typos? Why wouldn't you use a set instead of enums?
Enums are useful when there is a finite number of variants to something. For example an enum of male, female, other
not sure how you would use a set here?
so in your example, I can make an enum:
class Sex(Enum):
male: 1
female: 2
other: 3
or I can make a set
sex = {'male', 'female', 'other'}
isn't one way shorter and more concise?
you can decide to arbitrarily use some specific strings as variants.
but that is not what male is. male is not the 4 characters m a l e. male is one of the 3 variants
The use of enum is typically not just to store a collection of values
Rather, it to have easily recognizable values that increase readabality
Server.py (Running on my aws ec2 instance)
import socket
s = socket.socket()
host = socket.gethostbyaddr('aws.ec2.public.ip')[0]
port = 12345
s.bind((host, port))
s.listen(5)
while True:
c, addr = s.accept()
print('Got connection from', addr)
c.send('Thank you for connecting'.encode())
c.close()
Client.py (Running on my local pc)
import socket
s = socket.socket()
host = socket.gethostbyaddr('aws.ec2.public.ip')[0]
port = 12345
s.connect((host, port))
print(s.recv(1024).decode())
s.close()
All inbound & outbound TCP traffic granted
The Server code shows no error.
But the Client code says
Traceback (most recent call last):
File "/Users/sohamjain/Desktop/client.py", line 7, in <module>
s.connect((host, port))
TimeoutError: [Errno 60] Operation timed out
>>>
Connecting to the EC2 instance via rdp client works perfectly
When I run both these scripts on local host they seem to work fine.
But in case of AWS EC2 Instance, it does not.
Where did I go wrong?
So it's there for code clarity and preventing typos?
yes, and because it makes it clear that it is a variant rather than a string. Because when I see a string, I expect it to get printed or manipulated in some other way
in what workflow would you not prefer it as a string?
being of type sex == 'male' vs. 'male'... is there a practical difference?
not sure what do you mean?
I'm struggling to see why you would prefer the enum Sex.Male vs. 'male'
And then when you evaluate Sex.Male, you will get 1, which is not very intuitive?
well, you can do Male = 'male' instead of Male = 1
if I see 'male' I do not know what the other options are
and I do not know where to look
@fossil jetty An enum type can only take on the defined values, whereas a string can be anything.
as well as str not being the logical type of the value.
Using enums to restrict the possible values decreases the risk of making a mistake.
the logical type has 3 variants (a cardinality of 3). A string has many more options and context often does not tell me what the variants are, and whether there is even a finite number of them
and also you can do things like
class Direction(Enum):
Up = Vector(0, 1)
Down = Vector(0, -1)
Left = Vector(-1, 0)
Right = Vector(1, 0)
``` which would be a pain with strings
Is the enum basically a frozendict?
it is a mappingproxy internally, and also has a list of member names, and a backward map as long as values are hashable
imagine a function that takes a sex parameter as a string - how can you communicate what the allowed values would be? you may need extra documentation
def func(sex: str):
"""
Allowed sex values: "male", "female", "other"
"""
...
maybe you should also mention whether they are case sensitive, and if you eventually decide to add another option, you risk having the docs go out-of-sync with the implementation, and it quickly becomes a mess
now consider this:
class Sex(Enum):
MALE = 0
FEMALE = 1
OTHER = 2
def func(sex: Sex):
all of those problems are suddenly avoided, and you cannot pass an invalid value
~ you can, but then you're passing an invalid type
the value here is arbitrary (an int flag), we do not actually care what the value is, as long as it's unique
values don't have to be arbitrary, but in this example they are
def func(sex: Union[Literal['male'], Literal['female'], Literal['other']]): ...
``` is an option, but ew
yeah, there's ways around it, but enums are very pleasant to work with in my experience
your editor will most likely offer you the members if you do Sex.
that alone is a massive benefit for me
yeah, enum is definitely better
hey
if "Access denied" in hey or "Error 502" in hey or "Deceptive site ahead" in now or "404 Error" in now:
#Links.pop(link)
array.append(link)```
what is the alternate way of writing this
probably more suited to #python-discussion, but you can do {"Error 502", "Access denied"}.intersection(hey) or {"Deceptive site ahead", "404 Error"}.intersection(now)
well, as long hey and now are not strings
can i do if ("Access denied" or "Error 502") in hey or ("Deceptive site ahead or "404 Error" ) in now:
When checking if something is equal to one thing or another, you might think that this is possible:
if favorite_fruit == 'grapefruit' or 'lemon':
print("That's a weird favorite fruit to have.")
After all, that's how you would normally phrase it in plain English. In Python, however, you have to have complete instructions on both sides of the logical operator.
So, if you want to check if something is equal to one thing or another, there are two common ways:
# Like this...
if favorite_fruit == 'grapefruit' or favorite_fruit == 'lemon':
print("That's a weird favorite fruit to have.")
# ...or like this.
if favorite_fruit in ('grapefruit', 'lemon'):
print("That's a weird favorite fruit to have.")
wait that’s not suitable here :p
In [45]: from itertools import count
...:
...: class IncrDict(dict):
...: def __init__(self, start=0, step=1):
...: self.counter = count(start, step)
...:
...: def __missing__(self, key):
...: if key.startswith('__'):
...: raise KeyError(key)
...:
...: self[key] = next(self.counter)
...: return self[key]
...:
...: class EnumMetaclass(type):
...: def __prepare__(*args, **kwargs):
...: start = kwargs.get('start', 0)
...: step = kwargs.get('step', 1)
...: return IncrDict(start, step)
...:
...: class Enum(metaclass=EnumMetaclass):
...: def __init_subclass__(cls, **kwargs):
...: """Pull kwargs from class def"""
...:
...: class Sex(Enum):
...: MALE
...: FEMALE
...: OTHER
In [46]: Sex.MALE, Sex.FEMALE, Sex.OTHER
Out[46]: (0, 1, 2)
ipython sessions go on for a while
i've gotten into quad digits
...: is how ipython shows continuing the same multiline input
Hi, I'm a bit confused about the following:
from abc import ABC, abstractmethod
from typing import Type
class Base(ABC):
@abstractmethod
def foo(self) -> None:
pass
def bar(self) -> None:
print("Base bar")
class Derived(Base):
def foo(self) -> None:
print("Derived foo")
def func(arg: Type[Base]):
arg.foo()
arg.bar()
func(Derived())
In principle this code works as expected, it prints Base bar and Derived foo, however, mypy complains about it:
test.py:22: error: Too few arguments for "foo" of "Base"
test.py:23: error: Too few arguments for "bar" of "Base"
test.py:26: error: Argument 1 to "func" has incompatible type "Derived"; expected "Type[Base]"
Found 3 errors in 1 file (checked 1 source file)
Even though I think I'm using Type as intended (see: https://docs.python.org/3/library/typing.html#typing.Type)
mypy being mypy again
And no, you are using it wrong
Type[Class] means Class, not instance
should be just arg: Base
I guess for my use case, I typically have a config file stored in config.yml.
When you parse it, you get a nested dict (and I convert to dot dict for convenience)
Then if I ever need to access something, I will do: config.sex.male
I guess I could convert the values of config.sex to enums? Not really sure
as a practical example, an enum might be:
class Colors(Enum):
BG_COLOR: 'red'
FG_COLOR: 'blue'
bg_color = Colors.BG_COLOR
Or it could be a dict?
bg_color = config.colors.bg_color
class Colors(Enum):
BG_COLOR: 'red'
FG_COLOR: 'blue'
``` this is not really an enum usecase
though I guess you can't store your Vector example above
that would be better as a dict
or a dataclass
just define your own __call__ for the metaclass
Enums are not about taking a string and giving it a value. An enum enumerates (lists all posible values) of some type. a Direction has 4 options, gender has 3, a boolean has 2, an ordering has 3, fingers on a hand have 5, teeth have ~40 IIRC,...
Maybe this is a better example:
class AvailableColors(Enum):
Red: 'red'
Blue: 'blue'
vs.
config.availablecolors = ['red', 'blue']
yeah, there an enum does make sense
so you can write something like
def func(color):
if color in config.availablecolors:
...
def func2(color):
if color in AvailableColors:
...
and they are functionally equivalent?
Except the Enum in this case can give you a typehint
def func(color: AvailableColors):
...
ye, and once again, makes it clear what options there are (though if you have it in config, I would say strings are fine if the config format does not support enums)
@subtle pike I am dinamically linking libpthread to create mutexes. I configured a robust mutex but I does not works with python threads. When creating threads directly with pthread_create, the robust mutex works.
class ColorDict(dict):
def __init__(self):
super().__init__()
self['colors'] = []
def __missing__(self, key):
if key.startswith('__'):
raise KeyError(key)
self['colors'].append(key)
class ColorMeta(type):
def __prepare__(*args):
return ColorDict()
def __call__(cls):
return cls.colors
class Colors(metaclass=ColorMeta):
...
class MyColors(Colors):
blue
red
orange
In [63]: MyColors()
Out[63]: ['blue', 'red', 'orange']
@subtle pike The robust mutex only works when the python main thread exists with errors
what do you want me to do with this information?
@deft pagoda Thanks, I hate it
I think it is not possible to detect a thread exited at low level
thredo library offers ways to handle errors in threads
python threads are not "real" threads and this will always be a thing as long as the GIL exists, so detecting it at low level is impossible
huh
Is there any problem in creating threads without using threading?
no idea, my knowledge about threads is not deep
I always thought they were just pooled interpreters on the same environment
@tacit hawk afaik, as long as the non-threading non-_thread threads are not using python, it's fine (otherwise this could cause issues with the GIL)
they're actual factual threads, but they still obey the gil
you can use lower-level libraries that are threaded, i think
say, written in c++ or something
As long as threads not spawned through _thread don't use any PyXX C methods, you're fine
i think so too
I think I need a C extension, at python level it will not be possible to use mutexes
Does anyone here have experience with C++/Python Extensions btw?
I'm including a class from a library, and when I use it in py_module.cpp (the main file for my code) it works perfectly fine.
When I move the code using it to another file, and use a header to let py_module use py_nitro_emulator, I get an error, saying the class is not a valid name:
(nitro_emulator is a class from a library)
src/py_nitro_emulator.cpp:9:5: error: ‘nitro_emulator’ does not name a type
9 | nitro_emulator* emulator;
| ^~~~~~~~~~~~~~
src/py_nitro_emulator.cpp: In function ‘int NitroEmulator_Init(PyObject_NitroEmulator*, PyObject*, PyObject*)’:
src/py_nitro_emulator.cpp:13:11: error: ‘struct PyObject_NitroEmulator’ has no member named ‘emulator’
13 | self->emulator = new nitro_emulator();
| ^~~~~~~~
src/py_nitro_emulator.cpp:13:26: error: expected type-specifier before ‘nitro_emulator’
13 | self->emulator = new nitro_emulator();
| ^~~~~~~~~~~~~~
error: command 'gcc' failed with exit status 1
thread_entry = c.CFUNCTYPE(c.c_void_p, c.py_object)(run)
thread_id = c.c_ulong(0)
my_mutex = mutex.DemoMutex('my_shared_mutex')
if (e := mutex._pt.pthread_create(c.byref(thread_id), None, thread_entry, c.py_object(my_mutex))) != 0:
my_mutex.close()
raise OSError(e, os.strerror(e))
thread_result = c.POINTER(c.c_int)()
if (e := mutex._pt.pthread_join(thread_id, c.byref(thread_result))) != 0:
my_mutex.close()
raise OSError(e, os.strerror(e))
print("Success")
my_mutex.close()
run is a python func, _pt is the libpthread library. The posix robust mutex can detect when threads exits without releasing a mutex, it then notifies the next thread that attemps to lock the mutex by returning the error EOWNERDEAD. This error is returned with posix threads but not with python threads
This is the run func
def run(my_mutex):
print("pthread thread started")
time.sleep(5)
my_mutex.lock()
print("Mutex locked")
# exit the thread without releasing the lock
mutex._pt.pthread_exit(None)
So with the subprocess module
we have Popen yeah
Now the system ive built with it has some context manager stuff so we close any open processes if the main area gets killed
but now, how does subprocess handle started processes if they don't get killed on exit
e.g just telling pycharm to restart the file without a context manager
🤔
subprocesses should get killed when the main process exits
hmm cool
def store(self, value: int): pass
def store_explicit(self, value: int, memory_order: int): pass
or
def store(self, value: int, memory_order: Optional[int] = None): pass
?
you cannot have two functions with the same name exist in the same namespace
python doesnt support overloading
def store(self, value: int): pass
def store(self, value: int, memory_order: int): pass
is
def store(self, value: int, memory_order: int): pass
yeah, its regular assignment like
a = 1
a = 2
sorry meant store_explicit for the second store
just realised
lemme edit rq
def store(self, value: int): pass def store_explicit(self, value: int, memory_order: int): passor
def store(self, value: int, memory_order: Optional[int] = None): pass
ok this
Depending on how it's handled inside, but making it optional doesn't look like a bad idea
@sacred tinsel type annotations do support overloading
def store(self, value: int, memory_order: int = atomic.memory_order_seq_cst)
i think there's a decorator you need for it
!d g typing.overload
@typing.overload```
The `@overload` decorator allows describing functions and methods that support multiple different combinations of argument types. A series of `@overload`-decorated definitions must be followed by exactly one non-`@overload`-decorated definition (for the same function/method). The `@overload`-decorated definitions are for the benefit of the type checker only, since they will be overwritten by the non-`@overload`-decorated definition, while the latter is used at runtime but should be ignored by a type checker. At runtime, calling a `@overload`-decorated function directly will raise [`NotImplementedError`](exceptions.html#NotImplementedError "NotImplementedError"). An example of overload that gives a more precise type than can be expressed using a union or a type variable:... [read more](https://docs.python.org/3/library/typing.html#typing.overload)
@sacred tinsel ^
memory_order is still a position argument in def store(self, value: int, memory_order: int = atomic.memory_order_seq_cst) right?
Well annotations for stubs and actual working overloading are a bit different
Yes, unless you tell it to be a keyword only arg with a * it'll also be positional
can i tell it to be only arg?
Positional only?
yeah
Yes, with a / in the signature
def a(p1, p2, /) like that?
They're still passable as positional args, just can't force it on older python versions
i'm doing this in c, and i don't want to have to figure out how to handle both cases 😂
ugh actually might have to cuz i can't force it as a kwarg either :/
maybe python will be kind enough to enforce it for me 🙂
This seems to be an... interesting auto generated module https://docs.python.org/3/library/symbol.html
any reason why it'd do type(0) and delete 2 references at the end of the module in the source?
I'd guess that the auto-gen doesn't know about __all__ so it deletes those 2 names so they aren't visible to make the name space cleaner
It also might not know int exists as something it can access. Although it does know about list so who knows
I have a pain question to ask you, do you have some documentation to learn C implements Python ?
I wish regex matches used __getitem__ to get the groups instead of group
They do, don't they?
!e
import re
m = re.match("(abc)(\\d+)", "abc42")
print(m[0])
print(m[1])
print(m[2])
@grave jolt :white_check_mark: Your eval job has completed with return code 0.
001 | abc42
002 | abc
003 | 42
Wtf
Named groups work with stringly indices
Now I have to refactor all my code.
@boreal umbra new in 3.6
@grave jolt yes I want another documentation
What do you mean by "another documentation"?
I see this one but I want other sources to see the difference @grave jolt
You can also just look at the implementation on GitHub
There's a bool that came out recently about the internals. No idea if it's good.
Book*
Apparently I don't say book so my keyboard went with bool.
@spark magnet are you a core developer?
Sorry if I've asked you before.
no, i'm not, but i have poked around in the internals a bit
Aha
(i always forget to use match[1] for groups also, thanks for the reminder)
On a semi related note, I'm using regex for a project
That project includes reading a lot of files
So I'm using cached attributes to make sure that the disk read doesn't happen until and unless you need the data from the file, rather than just its metadata
But I'd need five cached attributes per file, so it would be choosing either zero to five reads per instance, or always one.
Unless I cache the content of the file as well
But now it seems like silliness.
@snow kindle which part do you want help with?
I have a pain question to ask you, do you have some documentation to learn C implements Python ?
@worldly venture ^^ 🙂
lol
cypt since you've already looked at the basic documentation, is this a question because you need help with something specific? or you just generally want more documentation to look at?
@boreal umbra why would it be five reads to get five attributes from one file
Presumably you would have one method to invalidate all 5 cached attributes, and reload them all at once from one read
@red solar integration of C class in python
You write a C program with enter value and I want to be able to get them in python
C doesn't have classes 🤔
has this function/class already been written in C, and you want to access it from python? or you're trying to write your own from scratch?
How do I convert a pil.image to a format image (jpeg)?
Image.save(name, format="jpg") (or something similar)
@snow kindle
has this function/class already been written in C, and you want to access it from python? or you're trying to write your own from scratch?
oh you're on, pinged you cuz thouhgt you might be on a while later lol
@red solar I write a function in C for get time right and I want to be able to import it in python
@red solar plt.savefig(buffer, format="jpg")?
i don't think you can import a single function from C - you can either compile your C function as part of a shared library, and access it through ctypes (in which case it will be a single function), or if you want to access it as normal python, you need to turn it into a module @snow kindle
uhhh
was thinking more https://pillow.readthedocs.io/en/stable/reference/Image.html#PIL.Image.Image.save kranthos
@red solar ok thanks
@red solar I couldn't make it work
here is my code (it's a mess so far)
plt.show()
buffer=BytesIO()
plt.savefig(buffer, format="jpg")
buffer.seek(0)
#buffer.save("hello.jpg")
im=PIL.Image.fromarray(buffer)
print(im)
im.save(buffer, "png")
print(im)
#image=PIL.Image.open(buffer)
#print(type(image))
print(type(buffer))
await ctx.send(file=discord.File(fp=plt, filename="hello.jpg")) ##works```
plt is a plot object
do you see where the problem is?
yes it does
bufferedio = BytesIO()
avatar.save(bufferedio, format="PNG")
bufferedio.seek(0)
then yeah, this but JPG
(or JPEG i forget)
(also instead of avatar, plt)
you said this was pil.image?
@red solar np, how can I solve it?
plt.show() # pyplot obj
buffer = BytesIO()
plt.savefig(buffer, format="JPEG")
buffer.seek(0)
file = discord.File(buffer, filename="hello.jpg")
await ctx.send(file=file, filename="hello.jpg"))
@red solar THE BOT sends an empty image
and the type of plt is module
which doesn't actually does what I want
async with ctx.typing():
# Get avatar bytes
image_bytes = await ctx.author.avatar_url.read()
avatar = Image.open(BytesIO(image_bytes))
avatar = avatar.convert("RGBA").resize((1024, 1024))
avatar = self.crop_avatar(avatar)
ring = Image.open(Path(f"bot/resources/pride/flags/{flag}.png")).resize((1024, 1024))
ring = ring.convert("RGBA")
ring = self.crop_ring(ring, pixels)
avatar.alpha_composite(ring, (0, 0))
bufferedio = BytesIO()
avatar.save(bufferedio, format="PNG")
bufferedio.seek(0)
file = discord.File(bufferedio, filename="pride_avatar.png") # Creates file to be used in embed
embed = discord.Embed(
name="Your Lovely Pride Avatar",
description=f"Here is your lovely avatar, surrounded by\n a beautiful {option} flag. Enjoy :D"
)
embed.set_image(url="attachment://pride_avatar.png")
embed.set_footer(text=f"Made by {ctx.author.display_name}", icon_url=ctx.author.avatar_url)
await ctx.send(file=file, embed=embed)
this is what i'm going off of 🤷
oh yeah wait
plt.show() # pyplot obj
buffer = BytesIO()
plt.savefig(buffer, format="JPEG")
buffer.seek(0)
file = discord.File(buffer, filename="hello.jpg")
await ctx.send(file=file))
i think specifying filename made it send an empty file if the filename didn't exist
@polar carbon
Anybody here familiar with DP utilization within python?
DP being decimal point? or something else?
dynamic programming
ok... you're going to have to elaborate on what you mean by that
it's not really on topic for this channel.
like everything in python is dynamic
Discussion on the use cases, implementation and future of the Python programming language including PEPs, advanced language concepts, new releases, the standard library, and the overall design of the language.
ah ok my bad
@red solar it refers to https://en.wikipedia.org/wiki/Dynamic_programming
though i dont think saying this falls within "implementation... of the Python programming language" is so farfetched
@grizzled vigil if you're on, ik you work mainly on async, but do you know if there's any good reason why python doesn't have any atomics support rn? only things i can think of are back in the day compiler support for stdatomic.h was lacking, and lack of interest
oh huh
it's an actual thing
though i dont think saying this falls within "implementation... of the Python programming language" is so farfetched
@signal eagle If you were asking whether CPython uses dynamic programming in its implementation, that's on-topic. If you're asking how to use dynamic programming in Python, that's not.
"implementation of the language", not "implementation in the language".
ah gotcha
@polar carbon probably time to ask in #discord-bots cuz that's as much as i know 😂
godlygeek isn't discord.py 🤨
what exception are you getting?
none
plt.show()
buffer = BytesIO()
plt.savefig(buffer, format="JPEG")
buffer.seek(0)
test_im = Image.open(buffer)
test_im.show()
try this
if it doesn't show the image again, then your issue is here
otherwise, it's with discord.py
How do I convert
BytesIOfile intoPNG? @raven ridge
@polar carbon That's off-topic for this channel.
ok... what about test_im.show()?
i am new here, what is all of this about?
anything python related pretty much
advanced python is
Discussion on the use cases, implementation and future of the Python programming language including PEPs, advanced language concepts, new releases, the standard library, and the overall design of the language.
np 🙂
im on the most boring project rn, i wrote this whole long ass vectorization function for a chat agent and now they are making me use dialog flow
and just throw away my work
Not "anything Python related" at all.
did you at least get paid for it tho? (also yeah that)
the topic is about the language itself.
no i meant the server there :/
yeah im paid hourly and get bonuses
yeah it was j annoying, i wanted to finish it so i could throw it on my application
for college that is, i am 18
how precisely does a vectorisation function relate to a chat agent.
it was for classifying intents and finding spelling errors
throw it on your application anyway 🤷
and you hand implemented something for that?
yeah
theres a lesson involved
what’s that
dont do that :p
it was pretty fun doing it tho
i know
ok... what about
test_im.show()?
@red solar that works
but my bot doesn't send it because it's aPIL.PNGIMAGEPLUGIN.PNGIMAGEFILE
then it's definitely a question for #discord-bots
thanks!
what are you doing kranthos
join him in #discord-bots and see 🙂
I don't know what sort of formatting logger is using for it's format = ... , presumably it's logger specific?
I was wondering how to zero pad some fields so it printed nicer, like f" {blah:<20}" or something, that sort of formatting, I'm not sure how to do this with %(levelname)s though
oh i can just do %(module)10s instead of %(module)s , i guess I'm not familiar with this version of string formatting
@magic python it's %-formatting
@spark magnet thanks, is it more limited than the typical formatting?
for example, can it left/right align?
what are you counting as "typical"?
sorry, typical for me is f-strings I suppose
thanks ned... is it odd that logging still uses this "old" style? I thought that everything would move as one, but i might just be wrong there
curious whether it's something that would be in line for updating at some point, or if it's just fine as is
you'd have to think about backward compatibility
You can actually get logging to use .format() style format strings, instead - but, don't. Every library out there that does any logging is doing it with printf-style format strings, and so if you set up logging to use .format() style format strings it will break whenever you try to use any library that anyone else wrote.
read https://docs.python.org/3/howto/logging-cookbook.html#formatting-styles and weep, heh
i believe the old style is lazyily evaluated in the sense that the formatting isnt done if the log level is unsufficient
that's true, but independent of style. It will be lazily evaluated if you use $ or { as your formatting style, too.
https://docs.python.org/3/howto/logging-cookbook.html#using-custom-message-objects - those are both lazy.
ah that seems quite a nice version. unfortunately ive seen log.info("blabla".format()) quite a lot. ill have to bookmark that as the right way to do it.
9 times out of 10 the str conversion isn't the expensive part of logging
so the lazy evaluation aspect never really helped me much in practice
since you're still computing all the arguments to the logger
i've definitely had it be the expensive part in other systems, but never in python. probably instincts sending me astray there.
the truth of the matter is i simply like printf style better and this is ammo :p
the lazy evaluation is beneficial for other reasons, not just performance. If the substitution fails, for instance, the logger logs that the string formatting failed and then swallows the exception. If you're doing the string formatting yourself, you'll get an exception that will break out of the current block and bubble up
the reason for my crusade is the common pattern ive seen is
i want to use format, the line gets too long and complex to use an inline "aaa".format inside the call to logger, so ill move that to a temporary variable called 'msg' or something.
it shits me up the wall.
lazy evaluation's behavior of printing out the exception and then swallowing it is usually better for you, because logging in error blocks can have a bug that goes unnoticed for a long time, and you don't want to compound an error if you can avoid it.
thats a good point, definitely have observed the cascading failure of a seldom used log in an exception handler.
@raven ridge maybe its just me but i often end up having to do non-trivial calculations just to log the output that i want
and things like type conversion is the least of my concerns (+ those errors should be caught in testing anyway)
timestamp = response.timestamp
logger.debug('Request timestamp: %s', timestamp.strftime(...))
unless there's a better way to do this
i cant think of an actual example from my code right now
hm. I'm thinking more like someone does:
def get_ip_address(host):
try:
return socket.gethostbyname(host)
except socket.gaierror:
logger.error("Failed to resolve host {host}".format(host=host))
return None
``` and then someone comes along and makes a change like:
```python
def get_ip_address(hostname):
try:
return socket.gethostbyname(hostname)
except socket.gaierror:
logger.error("Failed to resolve host {host}".format(hostname=hostname))
return None
and misses updating the format string even though they renamed the variable everywhere else. I've seen mistakes like that happen all too often, and they're pretty easy to make.
sure, that should get caught in testing, but error paths tend to be less well tested than success paths, and in this case the original author may have tested it, and it was valid when it was written, but it became incorrect after the fact.
@paper echo the strftime thing isn't a great example, since the default __str__ of a datetime.datetime is pretty reasonable, but yeah, I do see your point. The exception swallowing can't prevent every possible error, only some.
yeah i agree its not a great example
Is there any way to hook into object deallocation from python?
@tacit hawk you mean like take some action when an object is garbage collected? Or something else?
yes, like a class destructor
if you mean for your own class, __del__ (though do note that it's not guaranteed to be called in all cases, and has some restrictions on what it can do).
I knew about __del__, I though there was another way. Thanks
well - why do you want another way?
there's https://docs.python.org/3/library/weakref.html#weakref.finalize as well.
if (!PyArg_ParseTupleAndKeywords(args, kwds, fmt, kwl, &PyLong_Type, &value_py, &mem_order)) { \
return NULL; \
} \
Py_INCREF(value_py); \
unsigned long long value = PyLong_AsUnsignedLongLong(value_py); \
Py_DECREF(value_py); \
any thoughts as to whether I actually need Py_INCREF and Py_DECREF here?
That finalizer seems to be more reliable than __del__
it has the exact same set of caveats, AFAIK
@red solar you don't, no. PyArg_ParseTupleAndKeywords gives you back a borrowed reference to value_py, and that borrowed reference is all you need.
ok awesome 🙂 i don't need to change all my functions 🙂 (there's like 10 that look just like this)
you may want to look into Cython, though. 🙂
yes.
hmm
Compile CPython extensions?
does Cython have macros or templates?
Fused types are not currently supported as attributes of extension types.
:/
also tbh i don't want a fused type :/
#define PY_ATOMIC_DEFINE_METHOD_SIGNED_OP(TYPE, TYPE_NAME, OP, OP_NAME) \
static PyObject* \
PyAtomic_##TYPE_NAME##_##OP_NAME(PyAtomic_##TYPE_NAME##_Object *self, PyObject *args, PyObject *kwds) \
{ \
long long value; \
int mem_order = memory_order_seq_cst; \
const char *fmt = "L|i"; \
static char *kwl[] = {"value", "memory_order", NULL}; \
if (!PyArg_ParseTupleAndKeywords(args, kwds, fmt, kwl, &value, &mem_order)) { \
return NULL; \
} \
if (value > SMAX_FAST_OF(TYPE) || value < SMIN_FAST_OF(TYPE)) { \
PyErr_SetString(PyExc_OverflowError, "Python int 'value' too large to convert to C " # TYPE); \
} \
else if (!is_valid_memory_order(mem_order)) { \
PyErr_SetString(PyExc_ValueError, "invalid 'memory_order' value"); \
} \
if (PyErr_Occurred() != NULL) { return NULL; } \
long long old_val = atomic_##OP##_explicit((volatile TYPE *) &self->value, (TYPE) value, mem_order); \
return PyLong_FromLongLong(old_val); \
}
like i have this
and i can use it here 🙂
and expand them all
not sure i can do that with fused types
hm - how many types do you need this for?
potentially all of these? realistically probably not
(although if any of them are wider than long long i'm kinda screwed)
tbh i'll probably do all of them just cuz i'm adding a from_addr() class method, and someone could provide an address to any of those types 🤷
is this code somewhere on github that I can play with it?
what i shared? or the list of types?
what you shared.
i literally started it today - i can send you a gist link when i finish if you want
I'd settle for a gist of what you have so far 🙂
although i usually write c++ - this is my first time intentionally writing C, and my first time with macros, so might be a little rough
ok one sec
you can write extensions in C++ instead if you prefer, FWIW
oh also my <limits.h> file for some reason doesn't actually provide min/max for any types
why are on earth are you making a macro like that
i realise, but i don't want to deal with linkers, and also c in an environment where destructors aren't important is fun 🙂
i'll send the link here, you can see for yourself lol
the macro doesn't look unreasonable to me at first glance.
ah see that's cuz you're used to C then 😂
That macro seems unwiedly lol
https://gist.github.com/doodspav/2a651243e42f4d2e4b4753e927d1328b i was also told that <limits.h> defined CHAR_BIT? but it didn't for me :/
How are you supposed to debug that?
uhhh
ask whoever debugs it 😂
i've managed to get it down to 2 warnings
and both of them are unavoidable
Its just so much. Lke you drop the baby all over the place and preprocessor aint gonna check crap
well i'm not writing this out normally
that's not my fault right?
({
__auto_type; //stuff
})
is the expansion of atomic_exchange_explicit macro provided by stdatomic.h
you realize that the GIL makes every int an atomic int, right?
not if you put it in shared memory
true enough.
any thoughts on the warning?
Looking at it...
I think you shouldn't have the cast to volatile, if I'm reading this right:
The argument is pointer to a volatile atomic type to accept addresses of both non-volatile and volatile (e.g. memory-mapped I/O) atomic objects, and volatile semantic is preserved when applying this operation to volatile atomic objects.
fetch_add/sub/or/xor/and also say that tho
ohhh i see what you mean :/ it should be volatile _Atomic(TYPE) *?
hmm nvm
(i mean that's not exactly what you said, but i what i followed on as)
i swear i got an error earlier for not casting it
ig i'll try again
I don't think there should be a cast to volatile at all
you think it should be a plain &self->value?
I don't think there should even be a cast - I think it should just be:
long long old_val = atomic_##OP##_explicit(&self->value, (TYPE) value, mem_order);
self->value is already of type _Atomic(TYPE), so the cast shouldn't be necessary - and it's not volatile, so casting it to volatile seems wrong...
what compiler are you testing with?
gcc 7.5.0
technically this is CLion (and not the compiler directly) but i trust it
exchange seems to want (TYPE *)
everything else wants (volatile TYPE *) (apart from store or load that uses exchange under the hood so it wants (TYPE *)
Hm, I'm using gcc 7.5.0 and don't even get that far...
py_atomics.c: In function ‘PyAtomic_Bool_FetchAdd’:
py_atomics.c:392:1: error: operand type ‘volatile _Bool *’ is incompatible with argument 1 of ‘__atomic_fetch_add’
PY_ATOMIC_DEFINE_ALL_UNSIGNED_METHODS_AND_STRUCT(_Bool, Bool)
you tried to compile it?
yeah - was the version you gisted not in a compilable state?
Fair enough, then. "Only one warning" made me think "and no errors" 😄
if you want i'll ping you when it's done and compiles with minimal errors for me
*warnings not errors
yeah, you have my curiosity. I've never seen something where Cython isn't the easiest path, and I'm intrigued by the challenge here.
I'm also not intimately familiar with the C atomics API.
me neither - and i'm still not happy with volatile on every operation
in c++ atomics get optimised out if they can
can't optimise out volatile :/
so the goal is to define a new Python type for each of the C atomic types, each having every operation in fetch_add/fetch_sub/fetch_or/fetch_xor/fetch_and/exchange/load/store/compare_exchange_weak/compare_exchange_strong/is_lock_free
at a minimum, yes
also getattr/setattr for value
and dunder methods
__(i/r)add/sub/or/xor/and__, __int__, probably some others
plays with Cython
i'm at 500 lines, and it's just macros - in cython it would be that much per type
no chance it happens in cython 😂
well, worst case scenario it wouldn't be the craziest thing in the world to use the preprocessor to generate a Cython file. Best of both worlds.
(Or, you know, generate it at build time in setup.py with Python code, if you're not a lunatic)
we not using __int__ anymore? or is this just __index__ calling it internally?
what is it with python and not caring about signed integers
it's like AsLongLong we'll convert it from non integer types, we'll do our best, overflow will throw an exception
AsUnsignedLongLong yeah you may get an overflow error at most
Hi
hi 🙂
@red solar I think I'm still not quite picturing how you plan to use this. the GIL gives atomicity for every integer operation, at least in CPython. You contended that it doesn't if the integer is in shared memory, which is true - the GIL only protects it within one process (and perhaps one day within one interpreter). But - you're defining the integer as a member variable of a Python object, which means it can't be in shared memory, anyway - it's on the interpreter's heap. What am I missing?
crap one sec
ok that shouldn't be much of an issue - just changes how i initialize my atomic class
to - what, allocate a page of shared memory via mmap in your __init__? And is the idea that you share it with child processes, or what?
yeah
A while ago I mentioned the idea of having a __key__ method for sorting so that you don't have to know anything about how the other operand in a comparison function
how does sorting currently work?
but do you have to know anything about the other operand for __lt__?
presumably you need to know its type
@red solar It uses a special sorting algorithm called timsort, and it uses how __lt__ is defined for a given class to decide when to swap things
I think it's similar to bubble sort but with certain optimizations in place to capitalize on common cases.
I'm trying to do a weird thing where the type isn't necessarily the same.
i don't see how using __key__ would be better than __lt__
@raven ridge you raise a good point tho, if i don't own the memory myself, then there's no need for the classes to be defined in terms of types - they can be defined in terms of signed/unsigned and width
(maybe they could originally)
hadn't thought of that
This website defines a system for annotating text: https://brat.nlplab.org/standoff.html
Description of the brat stand-off annotation format.
Consider the entities, which are just labels in the text
and relations, which state that two entities are related in some way
@boreal umbra the type isn't required to be the same for < - when you do a < b Python will call a.__lt__(b) and if that returns NotImplemented it will call b.__gt__(a), so if either of the types knows how to deal with the other it's fine
I want relations to sort themselves based on however the two entities that comprise it are sorted, even if someone wants to redefine how they're sorted in their own implementation.
what was wrong with sort(list, key=func)?
I think it would be more concise if the key for a given class was part of the class
I suppose __lt__ is basically that
the problem with __key__ is, what would you do if the list contained some elements that had __key__ defined and some that didn't?
if __lt__ is basically comparing tuples of different instance variables, it sort of violates DRY
nothing stops you from defining a def sort_key() as a @classmethod or @staticmethod and then doing sort(lst, key=MyClass.sort_key)
all __lt__ is doing is x < y - how that's doing is up to the implementation
well that would be the same as __key__, just not using dunder naming.
no, it wouldn't be, because you have to tell it which class's sort key to use.
oh, I was already doing that in my code. I guess I could have said that.
the point of the magic methods is that the interpreter looks them up automatically. The point I'm making is that it wouldn't make sense for the interpreter to automatically look up a __key__, because it's not clear what it could do if some elements in the list don't have an __key__
well, isn't it the same with __lt__?
well, in that case it falls back to __gt__ with the arguments reversed.
no - a < b implies that b > a
ah yes
but, anyway - if some members of a list defined __lt__ but not __key__, and others defined __key__ but not __lt__, what's a sort() to do?
it could try __key__ first, then __lt__, etc.
I guess you'd end up with attribute errors that you'd have to handle
so that would be annoying
If class A has only __key__ and class B has only __lt__, then each of them thinks they've done the right thing, but there's still no sane ordering for sorted([A(), B()])
I think this really stems from the fact that I don't like how you have to have multiple copies of the same tuple for identity-related functions
which tuple?
__eq__, __hash__, etc will all probably use the same tuple, so you'd have to have several copies of it, and I'm not aware of IDEs handling that
a tuple of whichever attributes define that instance.
usually that's made up on the fly in each method - creating a tuple is one of the cheapest things you can do in Python
^
also won't they generally just copy the pointer? rather than make a whole new object?
(unless it's immutable or something)
I'm talking about the writing of the code rather than ease of execution.
ah. well, dataclasses can help a bit with that.
i don't see how writing the code for __hash__ is at all similar to something like __eq__
but also pycharm writes a few of them for you if you ask it to
class Person:
def __init__(self, name, age):
...
def __hash__(self):
return hash((self.name, self.age))
def __eq__(self, other):
return (self.name, self.age) == (other.name, other.age)
etc
ah i see
I think there's a way to pick and choose which fields in a dataclass you want to get included in all that
yeah, there is.
but you might not want them to be in the same order for sorting.
The order of the fields in all of the generated methods is the order in which they appear in the class definition.
So you have control over that, too.
for equality and hash the order obviously doesn't matter
but I might want the order for sorting and the order in the class definition to be different.
different to what?
different to each other.
it affects the repr as well... and the order of arguments for __init__
yeah
ok but do those matter?
yes
the most intuitive order for the human-readable content isn't necessarily the best order for sorting it.
i disagree, in general the most intuitive order is the most important first, which is generally the best order for sorting too
If __sort_key__ had existed from the start, I think I'd agree with you that it would be better. I just can't imagine a way for the interpreter to switch to that without causing a backwards compatibility nightmare.
yeah, the backwards compatibility would be a big issue.
if they make a call for breaking changes because they decide to release python4, there's your chance 🙂
@red solar the content I'm trying to represent is defined on that website I mentioned earlier, and it reads lines of documents where the order is string, int, int, string
but the two ints are what you'd want to use for sorting, followed by the first string
and the last string doesn't matter at all.
Sorting it would be completely pointless if it sorted it in the same order that the data is presented.
ahh ok
https://www.python.org/dev/peps/pep-0440/#version-epochs is an example of a place where the most important field is one that you would want to be optional in a __init__.
Python version numbers are sorted first by epoch, but you don't want to make epoch your first argument to __init__, because for almost every package in existence it's 0, and it's an extreme special case that anything ever uses anything else, so you'd want your constructor to do epoch=0 to set the sane default.
// module: atomic
class Uint:
def __init__(self, ptr_or_memview, size: int, offset: int = 0):
pass
@raven ridge as a constructor?
can you think of anything else you'd want to construct it from?
signedness?
UInt and Int are separate classes
ah - should they be?
(and there's also Buffer and Flag)
yes
they have different characteristics (well kinda, signed integer overflow is defined for atomics)
but anyway, it has to be a separate class due to implementation issues
hmm... ok none ig, you could just check a flag - lets go with "i don't like that implementation" 😂
you need to keep track of it somehow tho
and i prefer as part of the type, than as an optional parameter that gets stored, and has to be checked on every function call
I'm not even clear on why you need to check it on every function call, though - the atomic_* functions are all overloaded for both signed and unsigned, so - beyond a cast to the appropriate type - what does signed vs unsigned change?
because we need to know what the appropriate type to cast to is
and we also need to cast the arguments to that type (e.g. if we're calling .store(value))
and converting python integers to int is completely different to converting them to unsigned
which is a right pain :/
signed is nice, it does conversions for us
unsigned does not 😒
but even without that it's still 2 different functions
and what's the point of offset - why not just make the user slice the memview themselves? Wouldn't that let you eliminate both the size and the offset arguments?
idk, maybe they're lazy, what can i say 🤷
maybe they're storing some random thing and using ctypes to access the internals
wouldn't you still be able to do that with just memoryview slicing? e.g.:
>>> m = memoryview(bytearray([0, 1, 2, 3, 4, 5, 6, 7]))
>>> list(m[:4])
[0, 1, 2, 3]
>>> list(m[4:])
[4, 5, 6, 7]
>>> list(m[0:2])
[0, 1]
>>> list(m[6:])
[6, 7]
yeah you would
so if that memory was supposed to be 4 int16_t they'd give you the last one with m[6:8], and you'd know the size is 2 and not need an offset. Likewise for 2 int32_t and m[4:8]
if it's a memoryview, the size and offset can be ignored - but i still want a way to create it from a pointer (just in case)
unless you can create a memoryview from pointer + size?
hmm ok maybe no offset
but yeah ctypes - if the pointer has no type (void), then size is used, otherwise it's ignored
why not just make them cast in that case?
cast in what case?
if they have a ctypes void pointer, why wouldn't you demand they cast it to a ctypes int pointer instead? Why allow them to pass pointer and size?
hmm... my reasoning was for 128 byte integers, lemme go check double's accuracy
a double can't even represent all 64-bit integers, let alone 128 bit ones.
^
yeah :/ could technically have it as 3 doubles? but probably not worth the conversion effort
isn't long long 64 bits?
minimum, yes
also for pointer types, ctypes only offers char *, wchar_t *, and void * :/
sorry if your question was is 64 bits enough, x64 offers atomic operations for 128bit ints
(well buffers, not really ints)
you can define your own types with it huh
I just hoped in the convo, what is the end-goal though?
ya know - stdatomic isn't guaranteed to be lock free, and if it needs a lock that isn't going to work across processes...
yeah ik, that's why we have is_lock_free 🙂 but i'm glad you saw that
which means we also need AlignmentError, and WidthError
ok so the downside is that if i want 128bit buffers to be atomic, i need to use intrinsics :/
on the plus side, i only need to support like 2 compilers and 1 architecture (x86_64)
@red solar I haven't tested this much, and need to go to bed, but check this out - https://github.com/godlygeek/py_atomics
It has a setup.py that generates a Cython file with one class for each of the signed and unsigned integer types of size 1, 2, 4, and 8, along with 2 classes called Int and Uint that wrap them, and calls the C stdatomic library in the implementation for each.
Reading it on mobile sucks, but on the plus side I finally understand mixins 🙂
You should be able to clone and build it with something like:
pip3 install --user cython
python3 setup.py build_ext --inplace
--user to install Cython into your home dir, drop that flag if you're installing in a virtualenv (probably obvious, but just in case)
I’ll do that in the morning, i’m in bed rn 😂
I tested it only about this much:
py_atomics>python3 -c 'from py_atomics import Uint; x = Uint(bytearray(b"\x80\x80")); print(int(x)); x.store(256*256); print(int(x)); x+=1; print(int(x))'
32896
Traceback (most recent call last):
File "<string>", line 1, in <module>
File "py_atomics.pyx", line 642, in py_atomics._DelegateToImplMixin.store
return self._impl.store(value, memory_order)
File "py_atomics.pyx", line 128, in py_atomics._uint16_t.store
uint16_t_store_explicit(self.ptr, value, memory_order)
OverflowError: value too large to convert to uint16_t
py_atomics>python3 -c 'from py_atomics import Uint; x = Uint(bytearray(b"\x80\x80")); print(int(x)); x.store(256*256-1); print(int(x)); x+=1; print(int(x))'
32896
65535
0
there's almost certainly bugs, but it seems to basically work.
updating dict be like:
not funky iterative python for other_dict in dicts: some_dict.update(other_dict) vs
funky and cool functional python any(map(some_dict.update, dicts))
what does the any do there
Making sure that the entire map object gets exhausted
ahh
The dict.update method returns None, so all return values are falsy
any will keep looking until it find a truthy one, which it never does
and so it runs to the end
why not list()
because they're only using it for the side effects. list would create an actual list will elements pointing to None before discarding it.
ah
I'm not saying I would write it like this, because this confusion is exactly why I think it's less readable
and the same with an iterator
list comprehension i mean
i keep pressing enter before i finish typing recently 🤔
@wide shuttle but it's funky!
how is it functional? It uses a function with a side effect, one of the least FP things out there
it has all the key criteria of being functional
"funky and cool" tends to be rather entertaining for the purposes of esoteric amusement and can sometimes be helpful for learning, but it's something you wouldn't even want to touch with a 10-foot pole in a remotely serious environment (open source contributions, work, etc.). I'm sure most already know this, but I just wanted to mention that for anyone who may not be. When I was first getting into intermediate programming, I was periodically guilty of trying to fit as much onto a single line as I could, using some needlessly convoluted lambdas, etc. without fully realizing how bad of a practice it would be in the real world.
its worse and harder to understand.
I think functional programming has it's place. I personally like to use it sometimes for quick real-time transformations where readability isn't particularly important (e.g. REPL or a script that only I'll use). It's also not bad when used for simple cases, it's just easy to get carried away with it
Functional solutions can be more readable and less bug-prone in certain contexts.
Where the functional abstraction neatly expresses the idea and the syntax doesn't allow you to do things wrong.
Definitely, every paradigm has a place somewhere
That being said, it's certainly not for everyone. There are some incredibly talented developers I've encountered that dislike it in pretty much any context, and that's perfectly fine. I think everyone has to figure it out for themselves to some degree rather than relying on broad generalizations about the paradigms. Not everyone is inclined to rationalize code in the same patterns.
it feels like a solution in search of a problem 9/10 times.
but i work on code and with teams of low to medium experience.
I worked with Java for 5 years, and the introduction of the Streams API (which is basically adding functional features, similar to LINQ in C#) were useful in lots of contexts.
Python comprehensions are a type of functional abstraction (albeit with a user-friendly interface) that's widely useful.
functional programming solves quite a few problems by creating constraints (pure functions, immutability, ...) and then solves the problems arising from that with other things. Which is why it can often seem like it solves made up problems.
yeah i wouldnt put list/gen comprehensions under this. they're gucci
Yeah, I've found myself using C#'s LINQ in production environments periodically, particularly for simple sorting and filtering purposes.
Chaining streams calls (and I know you can do similar things with LINQ) feels like it replaces 9/10 foreach loops.
in an objectively better way
My only dislike is that it can become difficult to debug when too many operations are chained (as can sometimes be common with "fluent interfaces"). But that's more on the user to make use of it responsibly, and separate it into multiple steps when needed.
kotlin takes that even further, to a sometimes ridiculous extent
If you have a particularly complex lambda in the middle of a chain, you can always just break it into a real function and give it a descriptive name
Then it's usually pretty easy to follow what happens in the chain
You'd end up with the same thing with a foreach loop, just spread over more lines, it feels like
If the logic is complex, it's gonna be complex in both cases
Yeah, I can agree w/ that sentiment. I suppose that I've just seen it abused more frequently, but that's more on the individual developers rather than the pattern itself.
You can absolutely abuse any language feature
