#esoteric-python
1 messages · Page 128 of 1
o.o
", ".join((lambda n:(lambda cb:setattr(cb,"__code__",cb.__code__.replace(co_code='d\x01}}\x00|\x00d\x02k\x05r\x0e|\x00d\x02\x18\x00\x04\x00}}\x00d\x03\x13\x00q\x02g{0}S\x00'.format(chr(n)).encode("latin1"),co_consts=(None,n,1,2),co_varnames=("x",),co_nlocals=1))or map(str,cb()[::-1]))(lambda:None))(10))
tias
show
!e ```py
", ".join((lambda n:(lambda cb:setattr(cb,"code",cb.code.replace(co_code='d\x01}}\x00|\x00d\x02k\x05r\x0e|\x00d\x02\x18\x00\x04\x00}}\x00d\x03\x13\x00q\x02g{0}S\x00'.format(chr(n)).encode("latin1"),co_consts=(None,n,1,2),co_varnames=("x",),co_nlocals=1))or map(str,cb()[::-1]))(lambda:None))(10))
@grave rover :warning: Your eval job timed out or ran out of memory.
[No output]
o.o
kek
!e import("sys").version_info
@grave rover :warning: Your eval job has completed with return code 0.
[No output]
hm
!e print(import("sys").version_info)
@grave rover :white_check_mark: Your eval job has completed with return code 0.
sys.version_info(major=3, minor=9, micro=6, releaselevel='final', serial=0)
hmm
hmmm
Python 3.10.0rc2+ (heads/3.10:c523022, Sep 26 2021, 15:34:18) [GCC 11.1.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> ", ".join((lambda n:(lambda cb:setattr(cb,"__code__",cb.__code__.replace(co_code='d\x01}}\x00|\x00d\x02k\x05r\x0e|\x00d\x02\x18\x00\x04\x00}}\x00d\x03\x13\x00q\x02g{0}S\x00'.format(chr(n)).encode("latin1"),co_consts=(None,n,1,2),co_varnames=("x",),co_nlocals=1))or map(str,cb()[::-1]))(lambda:None))(10))
'0, 1, 4, 9, 16, 25, 36, 49, 64, 81'
works on py10
hmmmm
as for the implementation: py startlbl = Label() endlbl = Label() ops = [ # x = n LOAD_CONST(n), STORE_FAST("x"), startlbl, # while x >= 0: LOAD_FAST("x"), LOAD_CONST(1), COMPARE_OP(">="), POP_JUMP_IF_FALSE(endlbl), # x = x - 1 LOAD_FAST("x"), LOAD_CONST(1), BINARY_SUBTRACT(), DUP_TOP(), STORE_FAST("x"), # push x**2 LOAD_CONST(2), BINARY_POWER(), JUMP_ABSOLUTE(startlbl), endlbl, # [...] BUILD_LIST(n), RETURN_VALUE() ]
]e
", ".join((lambda n:(lambda cb:setattr(cb,"__code__",cb.__code__.replace(co_code='d\x01}}\x00|\x00d\x02k\x05r\x0e|\x00d\x02\x18\x00\x04\x00}}\x00d\x03\x13\x00q\x02g{0}S\x00'.format(chr(n)).encode("latin1"),co_consts=(None,n,1,2),co_varnames=("x",),co_nlocals=1))or map(str,cb()[::-1]))(lambda:None))(10))
You can only use this command in the following channel(s): #878695047112040448, #476196062214750219, #310-release-stream, #bot-commands
o yeah
Sorry if I typed on the wrong channel. How do I detect that a function or method returns a value or just runs a code?
do you have the function as source code or function object
i have a function object
# pip install git+https://github.com/martmists-gh/pyasm
from asm import Deserializer, RETURN_VALUE, LOAD_CONST
def returns_value(func):
d = Deserializer(func.__code__)
ops = d.deserialize()
returns = [op for op in ops if isinstance(op, RETURN_VALUE)]
for i, op in enumerate(ops):
if op in returns and (not isinstance(ops[i-1], LOAD_CONST) or ops[i-1].arg != None):
# None is returned by default, but that's not the case here -> returning something else!
return True
return False
``` I think this *should* work, but it's untested code
it treats no return, return and return None as not returning a value
thanks you!
hello, this code gets an error
Traceback (most recent call last):
File "/home/thekralgame/PycharmProjects/test/node_editor_test.py", line 2, in <module>
from asm import Deserializer, RETURN_VALUE, LOAD_CONST
File "/home/thekralgame/PycharmProjects/test/venv/lib/python3.8/site-packages/asm/__init__.py", line 2, in <module>
from asm.serializer import Serializer, Deserializer, Label
File "/home/thekralgame/PycharmProjects/test/venv/lib/python3.8/site-packages/asm/serializer.py", line 7, in <module>
from asm.stack_check import StackChecker
File "/home/thekralgame/PycharmProjects/test/venv/lib/python3.8/site-packages/asm/stack_check.py", line 9, in <module>
opmap["JUMP_IF_NOT_EXC_MATCH"],
KeyError: 'JUMP_IF_NOT_EXC_MATCH'
Code:
from asm import Deserializer, RETURN_VALUE, LOAD_CONST
def returns_value(func):
d = Deserializer(func.__code__)
ops = d.deserialize()
returns = [op for op in ops if isinstance(op, RETURN_VALUE)]
for i, op in enumerate(ops):
if op in returns and (not isinstance(ops[i-1], LOAD_CONST) or ops[i-1].arg != None):
# None is returned by default, but that's not the case here -> returning something else!
return True
return False
def foo():
print("Foo")
print(returns_value(foo))
Hey, I reckon ast can be used to (ab)use stuff for this channel.
okay
i tried in python 3.9
Traceback (most recent call last):
File "/home/thekralgame/PycharmProjects/test/test.py", line 1, in <module>
from asm import Deserializer, RETURN_VALUE, LOAD_CONST
File "/home/thekralgame/PycharmProjects/test/venv/lib/python3.9/site-packages/asm/__init__.py", line 4, in <module>
__all__ = tuple(dis.opmap.keys()) + (
NameError: name 'dis' is not defined
okay i fixed
you need to write "import dis" to asm/__init__.py
@sick hound :white_check_mark: Your eval job has completed with return code 0.
Hello, World!
anyone up for some code golfing ?
https://www.hackerrank.com/challenges/python-lists/problem
my solution
a=[]
for _ in[input()for _ in[1]*int(input())]:_=_.split();getattr(a,_[0])(*[*map(int,_[1:])])if _[0]!="print"else print(a)
oh whoops I'll get on it
yeah I fixed that a few hours ago
@signal violet reinstall with pip and it should work
a=[]
for _ in[1]*int(input()):_=input().split();getattr(a,_[0],lambda:print(a))(*map(int,_[1:]))
96 bytes @onyx jacinth
Best I could do was```py
l=[]
for _ in[0]*int(input()):(lambda x,*y:getattr(l,x,lambda:print(l))(*map(int,y))))(*input.split())
wont this error when you call that lambda with parameters
oh wait but there are none ignore me
a=[]
for _ in[1]*int(input()):x,*y=input().split();getattr(a,x,lambda:print(a))(*map(int,y))
91 bytes, thanks for the destructuring trick
np
missing parens on the last input
ah
for a in[[]]*int(input()):f,*r=input().split();getattr(a,f,lambda:print(a))(*map(int,r))
``` 88 bytes
whats the challenge
for(a)in[[]]*int(input()):f,*r=input().split();vars(list).get(f,print)(a,*map(int,r))
``` 85 bytes
hey. i just started code golfing
how do you guys calculate size sys.getsizeof ?
I just get the length of the string
sys.getsizeof is for getting internal object sizes
Typical golfing rules measure the textual representation of the code in bytes not the compiled code
that's disgusting, I love it
I'd implement that in my interpreter thing but it'd effectively be the same code
you could, just that your language would now be ".pyc compiled file" instead of "Python"
also... why? it'd be bigger
Where did you have to add the code?
Imma try and write a runtime patch to enable that
@sick hound where is that token number found?
sith magic
time for a pull request
it'd be fun to see what the reason for rejecting it is, at least
is ! like not or ~ ?
The fact that it's a grammar change not accompanied by a pep?
could be that
how do i code esoteric python? is there a guide on how to write it?
By practicing python for a while
>>> from decimal import Decimal
>>> from asm_hook import *
>>>
>>> class d(c_double):pass # hack to delay converters
...
>>> @hook(pythonapi.PyFloat_FromDouble, restype=py_object, argtypes=[d])
... def pyfloat_fromdouble(doub):
... return Decimal(doub.value)
...
>>> @hook(pythonapi.PyFloat_FromString, restype=py_object, argtypes=[py_object])
... def pyfloat_fromstring(string):
... return Decimal(string)
...
>>> 1.0
Decimal('1')
>>> 1.5
Decimal('1.5')
>>> 2.5
Decimal('2.5')
>>> ``` was able to get float literals to be `Decimal`s automatically in the interpreter
is there any package that doesn't include workarounds for numpy (this is in the dill Pickler class)
and taking exorbitant amounts of psychoactive substances
should I dm him for the guide?
its on their github
random thought experiment, does anyone know how to create a NaN float value in python without a) importing anything (including cheaty ways to import things), or b) without just doing float("nan")
like, are there any builtin arithmetic operations that can result in nan?
(also doing float("inf") to do math with is not allowed)
!e print(1e999/1e999)
@rugged sparrow :white_check_mark: Your eval job has completed with return code 0.
nan
noice
that was quick heh
TIL 1e999 is a quick way to get inf
1e999*0 also works
I made a bit of a guide over at https://github.com/IFcoltransG/esoteric-python-guide
I am currently working out how to use the tokenize module to change python's syntax
Has anyone done this before?
From memory people tried to fit Haskell syntax into Python function bodies at some point.
Do you have source?
nice dude
is it linking to a different repo? i can't tell on here
oh its linking to wiki
yea source would be nice ☝️
Here's what I dredged up. Varying progress. #esoteric-python message
#esoteric-python message
That's impressive, no source though
This is really not that hard
Can't do it rn as I'm about to be in class but I might do it later
x=object()
print(((x==x)<<((x==x)<<(x==x)+(x==x)))-(x==x))
As someone who is totally ignorant of / uninterested in this stuff, I clicked that link for some reason, and I must say it was a really good read. Well done!
!e ```py
print(ord(""))
import sys;print(sys.version_info.minor+sys.version_info.micro)
@next flame :white_check_mark: Your eval job has completed with return code 0.
001 | 15
002 | 15
decrypt__it = 'but you are total trashes'
____ = decrypt__it.split(' ')
getattr(__import__(True.__class__.__name__[1] + [].__class__.__name__[2]), 'write')(1, eval(eval('____[0][0] + ____[1][0] + ____[3][0] + ____[4][5:]'))(''.join(chr(i) for i in [int((4-4+2**4+4-2//5-4/4%2-4/4//2%5-1**4+4/2-4%4*2+5+4//4*2%4//4-2%5+1+3) - -45.0), int((1+1*5-1*1//5-3-1**1-5-1*1/5//3%2+1-1/5%1+1*5//3**1**1*5%1+1+5*3//2+4) - -91.2), int((3%3/1-3/3%1-2+3//3/1%3//3//1-2-5**3%3+1-3/3/1%2+3+3%1+3+3%1%2//5-4) - -112.0), int((3%3/1-3/3%1-2+3//3/1%3//3//1-2-5**3%3+1-3/3/1%2+3+3%1+3+3%1%2//5-4) - -112.0), int((4//4**2%4%4+2**5-4%4**2+4*4//2-5/3**4-4-2-4-4**2/5%4+4/2**4*4-2/5+3//1) - -84.66172839506173), int((5%5%3**5**5//3%1%5-5%3+5-5-3-1/2//5//5%3%5-5+3//1%5%5+3**5/5//3%1-2//4) - -39.0), int((2-2+4+2+2/4//3*2//2%4+2//2**4%3**1-2%2%4-2+2//4//3+2-2**4/2*2**4+3//1+5) - -201.0), int((4//4**2%4%4+2**5-4%4**2+4*4//2-5/3**4-4-2-4-4**2/5%4+4/2**4*4-2/5+3//1) - -84.66172839506173), int((3%3%2*3+3%2-5%3-3/2//3%3/2/5**4*3*3%2//3*3-2+5%3-3/2**3+3-2/5/4%1) - -112.475), int((3%3/1-3/3%1-2+3//3/1%3//3//1-2-5**3%3+1-3/3/1%2+3+3%1+3+3%1%2//5-4) - -112.0), int((1-1%5**1//1%5*3/1*1-5%1/1**5+3+2**1-1//5%1-1+5//3*1/1**5-1**1%5*3**2+4) - -102.0), int((1**1/3%1/1//3-4/1-1+3*1**1%3//4/5+1+1*3**1//1**3/4*1**1*3//1**1*3%4*5**2) - 13.0)]), 'ascii'))```
P.S. the string was only putted there to roast my school teacher. Nothing to do with you guys 
yo
yo
Thank you, I had fun writing them.
!e
print(len(' '))
@golden finch :white_check_mark: Your eval job has completed with return code 0.
15
unicode characters
so no strings at all?
@sick hound
or did you mean
only printable ascii (32 to 126) allowed
this still works btw
just replace the spaces with literally any other character
!e print(["","","","","","","","","","","","","","",""].count(""))
@next flame :white_check_mark: Your eval job has completed with return code 0.
15
this is the loophole channel lmao
@sick hound This doesn't actually do what you asked, but it's related.
(It has len in it a lot.)
that's... art
It took a lot of work from the people in this channel.
!e Anyway...
print(((lambda z: lambda *x, **y: z)(None)).__code__.co_flags & (lambda *x, **y: x).__code__.co_flags)
@snow beacon :white_check_mark: Your eval job has completed with return code 0.
15
@sick hound :white_check_mark: Your eval job has completed with return code 0.
001 | 15
002 | 53
That looks awfully complicated. I think I prefer my method.
Isn't the second one equivalent to is?
is does not do ==, that's what == is for.
I'm not sure I follow.
is literally does pointer comparision
every object in python is a pyobject*
and id just returns the pointer as an int
thus is and ids are exactly the same (at least in cpython)
@sick hound
class A:
def __hash__(self):
return random.randint(1,6)
Unless you're meaning how two ids greater than 256 can be == to each other, but not is to each other? It's a bit strange to compare integers using is though.
Implementing a hashtable, perhaps.
My hesitance was that ids could be id(x) is id(y), in which case it would not be equivalent to is.
That looks like is then.
It's good. After all, doesn't the Zen say something about how many ways to do something there should be?
!e class A: def init(self, x): self.thing = x
@next flame :x: Your eval job has completed with return code 1.
001 | File "<string>", line 1
002 | class A: def __init__(self, x): self.thing = x
003 | ^
004 | SyntaxError: invalid syntax
hm
man not obsessed with original python's esotericness, so he started editing the source
This is most definitely not the channel to get help in haha
where should i , mention it please
Check out #❓|how-to-get-help
i've already done that
And you got to this channel?
one of these days we should bring a newbie straight to the dark side - it'd be interesting to see how they codde
Ooh yes that could be interesting
we'd need one who is already sensitive to the darkness
grab an asm programmer and throw them straight into esoteric
!e
>>> print({(1<<5)+(1<<4)+(1<<1):'Hello, World!'}[((-1[=-2)*[50])[0]])
Hello, World!
@sick hound :x: Your eval job has completed with return code 1.
001 | File "<string>", line 1
002 | >>> print({(1<<5)+(1<<4)+(1<<1):'Hello, World!'}[((-1[=-2)*[50])[0]])
003 | ^
004 | SyntaxError: invalid syntax
Oh ur code didn’t work
so, so many things are wrong with that
It would have worked for them, since they edited the Python interpreter to make it work
it's more like stealing their soul
why i get syntaxerror in slice using star expressions like that?
>>> tuple[*[str]*12]
File "<stdin>", line 1
tuple[*[str]*12]
^
SyntaxError: invalid syntax```and in functions this work????
```py
>>> def a(x, b, c):
... ...
...
>>> a(*[1]*3)```

this is probably fixed on 3.11 with pep 646(??)
@cerulean rivetin the meantime, tuple[(*[str]*12,)] does the same thing. On that note, what hellish thing are you working that a 12 tuple of strings is the correct data structure
I didn't understand why your code works, can you explain it to me?
essentially, a[1, 2, 3] just passes the tuple (1, 2, 3) to __getitem__, which is the same as what happens when you pass a tuple directly
In [2]: dis.dis('a[b, c]')
1 0 LOAD_NAME 0 (a)
2 LOAD_NAME 1 (b)
4 LOAD_NAME 2 (c)
6 BUILD_TUPLE 2
8 BINARY_SUBSCR
10 RETURN_VALUE
In [3]: dis.dis('a[(b, c)]')
1 0 LOAD_NAME 0 (a)
2 LOAD_NAME 1 (b)
4 LOAD_NAME 2 (c)
6 BUILD_TUPLE 2
8 BINARY_SUBSCR
10 RETURN_VALUE
```as you can see in the bytecode
Is it possible to redefine operators / define new operators without rebuilding the interpreter?
Like with ctypes or an interpreter hook or something
redefine, yes. define new - for some very limited subsets
@prisma coral
redefinition is just dunder modification
new operators are a much nastier kettle of fish, and usually need either direct bytecode modification or messing with the AST/tokenizer/lexer
if a newb appears with decent skills we can bring them to the shadow realm
I'd be interested to see a comparison of the baseline rate of mental disorders in this channel compared to the rest of the server
Thanks. Which dunders would you modify? Let’s pretend that I want to swap __sub__ with __mul__ for all objects
example of the dunder redefinition:
std,cout=type('',(),{'__getitem__':lambda self,name:type('',(),{'__lshift__':lambda self,data:(__import__('sys').stdout.write(str(data)),self)[1],'__repr__':lambda self:''})()})(),0
#include <iostream>
a=2
b=502
std[::cout] << a << b;
all objects?
!pypi fishhook
my approach would be to use a variation on sys.settrace to repeatedly perform that on all objects using fishhook
cool
type(None).__bool__.__doc__ is "self != 0" for some reason.
It's not often that you find an instance of NoneType that's equal to zero, I guess.
>>> type(None).__bool__.__doc_
Traceback (most recent call last):
File "<pyshell#0>", line 1, in <module>
type(None).__bool__.__doc_
AttributeError: 'wrapper_descriptor' object has no attribute '__doc_'
not for me
Is it an IPython thing then?
!e
print(type(None).__bool__.__doc_)
@golden finch :x: Your eval job has completed with return code 1.
001 | Traceback (most recent call last):
002 | File "<string>", line 1, in <module>
003 | AttributeError: 'wrapper_descriptor' object has no attribute '__doc_'
>>> type(None).__bool__(None)
False
It works on my machine.
Python 3.7 and Python 3.9, the latter with and without IPython. I wonder why that is.
!e print(type(None).bool.doc)
@rugged sparrow :white_check_mark: Your eval job has completed with return code 0.
self != 0
@golden finch you had a typo
right
can confirm it's that
>>> type(None).__bool__.__doc__
'self != 0'
that's unusual
maybe type(None).__bool__=lambda self:self!=0
it appears to be the same for all types
Objects/typeobject.c lines 8020 to 8021
UNSLOT("__bool__", nb_bool, slot_nb_bool, wrap_inquirypred,
"self != 0"),```
thanks
interesting
I wonder why that is
equalzero=lambda self:eval(type(None).__bool__.__doc__)
Originally, the bool method in Py2 was __nonzero__, and in both versions if you don't define the magic method Python will try checking len() == 0. So it might be a remmnant of that.
That's interesting.
Note that if
__len__is defined and__bool__is not, the boolean value is implemented aslen(self) > 0.
I'm trying to golf a raytracer I wrote, I've got a 3d vector represented as [x,y,z], what's the shortest way to normalise it? the current best I've got is this (where r is the vector)
map(lambda x:x/sum(map(lambda n:n*n,r))**.5,r)
oh yeah sum(n*n for n in a) is shorter than sum(map(lambda n:n*n,r))
s=sum(x**2for x in r)**-.5
map(s.__mul__,r)
the last bit is longer than ideal but I like it
I'm personally a big fan of map(dunder, ...)
[m/sum(n*n for n in r)**.5for m in r]
I think this is the shortest possible
I might use that map dunder thing somewhere else though
@grave rover im gonna use your pyasm to implement tail call recursion optimization
Oh no
Good luck :)
Feel free to report any issues you find
(note: not on pypi yet)
ah ill need to clone the github then
Pypi package seems to be a squat but made an ownership request already
out = Value.replace('€', '')
if 'M' in out:
out = float(out.replace('M', ''))*1000000
elif 'K' in Value:
out = float(out.replace('K', ''))*1000
return float(out)``` How can I do its reversion?
#❓|how-to-get-help @opaque fossil
we need a special copypasta for this channel about why you don't get help here
https://stackoverflow.com/questions/1732348/regex-match-open-tags-except-xhtml-self-contained-tags
something akin to this
~~ I need help with this ~~
print("hello world")
make ugly
that's cool
!e
with open(1, "w") as f:
import operator
operator.methodcaller("write", "Hello world").__call__(f)
@rapid sparrow :white_check_mark: Your eval job has completed with return code 0.
Hello world
why choose this channel for assistance...
edit -- as a bonus, if you run it interactively, it makes the interpreter useless
i had to use kill -9 to close it .
sigh
check pins
#esoteric-python message
im crying rrn
oh the jaws of defeat
tears of joy i presume
it's so much black magic and I have no idea what any of it does
@thorny tapir i can make it uglier i think
!e
exec(bytes('牰湩⡴栢汥潬眠牯摬⤢', 'u16')[2:])
@thorny tapir
@tepid otter :white_check_mark: Your eval job has completed with return code 0.
hello world
my superior hello world code
low-level language
its what computers interpret into binary basically
well a few levels above that
you can see the translated bytecode of a function through some roundabout methods
!e
print((lambda: 0).__code__.co_code.decode('u8'))
@tepid otter :white_check_mark: Your eval job has completed with return code 0.
dS�
@thorny tapir this is bytecode
d i believe is return
thats all i really remember
that roughly translated to "return 0" afaik
o ok
!e ```py
import('dis').dis(lambda: 0)
@snow beacon :white_check_mark: Your eval job has completed with return code 0.
001 | 1 0 LOAD_CONST 1 (0)
002 | 2 RETURN_VALUE
It looks like the d is the LOAD_CONST, and the S is the RETURN_VALUE.
!e
import dis
def f(): ...
dis.dis(f)
@tepid otter :white_check_mark: Your eval job has completed with return code 0.
001 | 3 0 LOAD_CONST 0 (None)
002 | 2 RETURN_VALUE
Same bytecode, different constant.
!e
import dis
def f(): ...
print(f.__code__.co_code.decode('u8'))
@tepid otter :white_check_mark: Your eval job has completed with return code 0.
d�S�
I don't think it usually encodes the constant itself in the bytecode. The number is usually an index in a tuple of constants that gets packaged into the function.
co_consts I believe.
yeah, correct
in this case, co_consts is the tuple (None,) and LOAD_CONST 0 loads co_consts[0]=(None,)[0]=None
the python compiler does many strange things in making bytecode
it should be possible though, correct
Not to my knowledge.
There's possibly some op that can construct values. You'd have to ask Chilaxan.
!e
from types import CodeType, FunctionType
from dis import dis
func = FunctionType(CodeType(*dict(co_argcount=1, co_posonlyargcount=0, co_kwonlyargcount=0, co_nlocals=1, co_stacksize=2, co_flags=67, co_code=b't\x00|\x00\x83\x01S\x00', co_consts=(None,), co_names=('eval',), co_varnames=('stmts',), co_filename='<stdin>', co_name='get_constant', co_firstlineno=1, co_lnotab=b'').values()), {'eval': eval})
print(func)
print(func.__code__)
print(dis(func))
arg = "('abc', 'def', 1, 2)"
print("construct value from %s" % repr(arg))
val = func(arg)
print("func(%s) -> %s" % (repr(arg), repr(val)))
print(val)
@rapid sparrow :white_check_mark: Your eval job has completed with return code 0.
001 | <function get_constant at 0x7f1b801e8a60>
002 | <code object get_constant at 0x7f1b8018f030, file "<stdin>", line 1>
003 | 1 0 LOAD_GLOBAL 0 (eval)
004 | 2 LOAD_FAST 0 (stmts)
005 | 4 CALL_FUNCTION 1
006 | 6 RETURN_VALUE
007 | None
008 | construct value from "('abc', 'def', 1, 2)"
009 | func("('abc', 'def', 1, 2)") -> ('abc', 'def', 1, 2)
010 | ('abc', 'def', 1, 2)
That's nice, although I was meaning something that doesn't need to read from args or constants.
can't you just fiddle with the stack a bit?
loading onto stack is tough but possible
Making a new version of the programming language isn't really in the spirit of solving a problem within the language under a constraint.
but thinking outside the box might be
ctypes
jk
frame->localsplus + frame->stacktop
hahaha, nice
very interesting, I didn't know about this behavior
sorry for the late reply, now that you said that, I thought about it tuple[(str,)*12]
oh yeah, that is nicer
wdym by esoteric python?
Needlessly complex hacky things such as #esoteric-python message
ohh hey apple!
been working on my code since you helped me
wanna see?
!e ```py
(
lambda _, , , ____, , , ______, ________: getattr(
import(True.class.name[] + [].class.name[]),
().class.eq.class.name[:]
+ ().iter().class.name[:][:__],
)(
_,
(lambda _, __, ___: (, __, ___))(
lambda _, __, : bytes([ % __]) + (, __, ___ // __)
if ___
else (lambda: _).code.co_lnotab,
_ << ___,
((( << __) + ) << (( << ____) - ))
+ ((((( << ) - ) << ) + ) << (( << ) + ( << )))
+ ((( << _) - ) << ((((( << ) + )) << ) + ( << )))
+ ((( << ) + ) << (( << ) + ))
+ ((( << ) - ) << (( << )))
+ ((( << ) - ) << (((( << ) + ) << ) - ))
- ( << (((( << ) - ) << ) + ))
+ ( << ((((( << ___) + _)) << _)))
- (((((( << ) + )) << ) + ) << (((( << ) + ) << )))
+ ((( << ) - ) << ((((( << ) + )) << )))
+ ((( << ) + ) << (( << )))
+ ( << _____)
+ ( << ___),
),
)
)(
*(lambda _, __, ___: (, __, ___))(
(
lambda _, , : [([(lambda: _).code.co_nlocals])]
+ (, __, ___[(lambda _: _).code.co_nlocals :])
if ___
else []
),
lambda _: _.code.co_argcount,
(
lambda _: _,
lambda _, __: _,
lambda _, __, ___: _,
lambda _, __, ___, ____: _,
lambda _, __, ___, ____, _____: _,
lambda _, __, ___, ____, _____, ______: _,
lambda _, __, ___, ____, _____, ______, _______: _,
lambda _, __, ___, ____, _____, ______, _______, ________: _,
),
)
)
@tacit cobalt :white_check_mark: Your eval job has completed with return code 0.
Hello world!
Python 3 version
Sure! Put it in an off-topic channel so as not to spam here 🍏
i came up with 2 versions of the spanish code
okkk
np
uhhh
which channel lol
python general
Yeah sure
I got a fun one too:
`XXX readobject called with exception set
Fatal Python error: pycore_interp_init: failed to initialize importlib
Python runtime state: preinitialized
IndexError: list index out of range
Current thread 0x0000007ff7a7dde0 (most recent call first):
<no Python frame>
`
looks like sys is imported before builtins, funnily enough
has anyone in this world found out how this works somehow
it's passing [1, 2, 3, 4, 5, 6, 7, 8] as the arguments
def <lambda>(1, 2, 3, 4, 5, 6, 7, 8):
return getattr(__import__((True).__class__.__name__[1] + [].__class__.__name__[2]), ().__class__.__eq__.__class__.__name__[:2] + ().__iter__().__class__.__name__[1:][5:8])(1, (lambda 1, 2, 3: 1(1, 2, 3))(lambda 1, 2, 3: bytes([3 % 2]) + 1(1, 2, 3//2) if 3 else (lambda : 1).__code__.co_lnotab, 1 << 8, ((5 << 4) + 1 << (3 << 5) - 3) + (((3 << 2) - 1 << 3) + 1 << (5 << 4) + (1 << 1)) + ((7 << 2) - 1 << ((1 << 3) + 1 << 3) + (1 << 1)) + ((7 << 3) + 1 << (1 << 6) + 1) + ((7 << 4) - 1 << (7 << 3)) + ((1 << 4) - 1 << ((3 << 2) + 1 << 2) - 1) - (7 << ((3 << 2) - 1 << 2) + 1) + (7 << ((1 << 3) + 1 << 2)) - (((1 << 3) + 1 << 2) + 1 << ((3 << 2) + 1 << 1)) + ((7 << 2) - 1 << ((1 << 3) + 1 << 1)) + ((3 << 3) + 1 << (5 << 1)) + (5 << 6) + (1 << 3)))
some math required .. I guess that's bit shifting
oh okay
this part is os.write:
__import__((True).__class__.__name__[1] + [].__class__.__name__[2]), ().__class__.__eq__.__class__.__name__[:2] + ().__iter__().__class__.__name__[1:][5:8]
k
reminds me of obfuscated javascript
it constructs a byte string using arithmetic & code objects shenanigans, and writes it to stdout
here's what some of the subexpressions evaluate to
yeah, haha
not sure exactly what they're doing with the huge numbers, unless it's making a number that represents "Hello world" when viewed as bytes
whoa thats ugly
but does it work on pypy
....no 😅
Traceback (most recent call last): File "prog.py", line 54, in <module> File "prog.py", line 29, in <lambda> + ().__iter__().__class__.__name__[_:][_____:________], AttributeError: 'module' object has no attribute 'incei'
that's neat
As a novice, This thread terrifies me.
@grave rover py def foo(a, b, c): print('ah') if a: return foo(1, 2, 3) else: return foo({}, sum([]), foo) breaks the serializer
>>> Serializer(Deserializer(foo.__code__).deserialize(), foo.__code__).serialize()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/Users/chilaxan/Desktop/Coding/python/pysnippets/tail_call_optimizer/asm/serializer.py", line 105, in serialize
data += x.serialize(self)
File "/Users/chilaxan/Desktop/Coding/python/pysnippets/tail_call_optimizer/asm/ops/abc.py", line 82, in serialize
return self.int_arg(self.arg)
File "/Users/chilaxan/Desktop/Coding/python/pysnippets/tail_call_optimizer/asm/ops/abc.py", line 18, in int_arg
return pack("Bb" if x < 0 else "BB", self.id, x)
TypeError: '<' not supported between instances of 'Label' and 'int'```
how would I bypass "SyntaxError: cannot assign to None"
or anything involving messing with None
I got the same error when adding the almost equal operator
I don’t remember how I fixed it
A case statement I’m guessing?
For the new node
Got it
I need to pick up on some basics of C tbh
so I can understand CPython source code
@sick hound here’s a hard challenge - add a null coalescing operator for python
!pep 505
I'm sure that's going to make it so readable
oh snap you're working on tail calls
lmao
"Are you really, REALLY sure you want to get the attribute??"
yep
maybe_none!!!.values()
what's the difference between this and
cond ? if-true : if-false
is that the same ?? operator?
oh ok, i see
i might want to use your cpython myself, if you implement any of that
i think the idea is it short-circuits the .c if a.b is None ?
that's a separate issue I guess
Gotcha, as expected it's friggin jumps
I hate these things
Well, I don't know if it's the right channel, but Esoteric python seems like it could answer questions about magic commands!
So I have many Jupyter notebooks with different widgets programs in it. Now, I wanna make a "Master notebook" and use the %run magic command to run the other scripts in the cells of my master notebook. The problem is that i have to pass a dataframe to the children widget programs and it seems like I can't.
I am running
%run widgets_program1.py {df}
in a cell, and it doesn't seem to be passing the variable. It seems he can only pass strings, but even if I do
df_string=dft.to_csv() #in the master notebook
%run widgets_program1.py {df_string}
and then
df=pd.read_csv(StringIO(sys.argv[1])) #in the children
it doesn't seem to work, infact if I print the lenght of sys.argv[1] in the children notebook it's completly wrong. Does anybody have a guess of why?
from asm import Deserializer, Serializer
def foo(a, b, c):
print('ah')
if a:
return foo(1, 2, 3)
else:
return foo({}, sum([]), foo)
if __name__ == "__main__":
d = Deserializer(foo.__code__)
ops = d.deserialize()
print(ops)
s = Serializer(ops, foo.__code__)
foo.__code__ = s.serialize()
``` this code runs fine for me
which python version are you on?
don't know the answer
depends on the bytecode
in that example (d = a?.b?.c ?? "") this would be the bytecode: py LOAD_FAST("a") DUP_TOP() LOAD_CONST(None) COMPARE_OP("is not") POP_JUMP_IF_FALSE(stringLabel) LOAD_ATTR("b") DUP_TOP() LOAD_CONST(None) COMPARE_OP("is not") POP_JUMP_IF_FALSE(stringLabel) LOAD_ATTR("c") DUP_TOP() LOAD_CONST(None) COMPARE_OP("is not") POP_JUMP_IF_FALSE(stringLabel) JUMP_ABSOLUTE(endLabel) stringLabel POP_TOP() LOAD_CONST("") endLabel STORE_FAST("d")
wait no
there we go
not sure how cpython does it but it can't be that different
but here's my suggestion; why not just make an import hook that modifies the source code and expands those
like a?.b is expanded to (a if a is None else a.b)
and <expr> ?? "abc" becomes (x if (x := <expr>) is not None else "abc") (where you'd need to change x to an unused name)
uhh
opcode or operator
I don't think you need a custom opcode for that
also what in the world is LOAD_ATTR_ADAPTIVE and all that
huh
it's not even in opcode.py
also not documented on the website
it's mentioned in PEP 659 but that one is still a draft
what
3.10 isn't even released yet afaik
oh no
time to figure out what opcodes were changed in 3.11
here's a few
the official release notes only mention CALL_METHOD_KW
@golden finch :white_check_mark: Your eval job has completed with return code 0.
POP_TOP ROT_TWO ROT_THREE DUP_TOP DUP_TOP_TWO ROT_FOUR NOP UNARY_POSITIVE UNARY_NEGATIVE UNARY_NOT UNARY_INVERT BINARY_MATRIX_MULTIPLY INPLACE_MATRIX_MULTIPLY BINARY_POWER BINARY_MULTIPLY BINARY_MODULO BINARY_ADD BINARY_SUBTRACT BINARY_SUBSCR BINARY_FLOOR_DIVIDE BINARY_TRUE_DIVIDE INPLACE_FLOOR_DIVIDE INPLACE_TRUE_DIVIDE RERAISE WITH_EXCEPT_START GET_AITER GET_ANEXT BEFORE_ASYNC_WITH END_ASYNC_FOR INPLACE_ADD INPLACE_SUBTRACT INPLACE_MULTIPLY INPLACE_MODULO STORE_SUBSCR DELETE_SUBSCR BINARY_LSHIFT BINARY_RSHIFT BINARY_AND BINARY_XOR BINARY_OR INPLACE_POWER GET_ITER GET_YIELD_FROM_ITER PRINT_EXPR LOAD_BUILD_CLASS YIELD_FROM GET_AWAITABLE LOAD_ASSERTION_ERROR INPLACE_LSHIFT INPLACE_RSHIFT INPLACE_AND INPLACE_XOR INPLACE_OR LIST_TO_TUPLE RETURN_VALUE IMPORT_STAR SETUP_ANNOTATIONS YIELD_VALUE POP_BLOCK POP_EXCEPT STORE_NAME DELETE_NAME UNPACK_SEQUENCE FOR_ITER UNPACK_EX STORE_ATTR DELETE_ATTR STORE_GLOBAL DELETE_GLOBAL LOAD_CONST LOAD_NAME BUILD_TUPLE BUILD_LIST BUILD_SET BUILD_MAP LOAD_AT
... (truncated - too long)
Full output: https://paste.pythondiscord.com/videbizipu.txt?noredirect
oh I use opcode.opmap
i have both versions , i could compare that
!e py print(__import__("opcode").opmap)
@grave rover :x: Your eval job has completed with return code 1.
001 | File "<string>", line 1
002 | py print(__import__("opcode").opmap)
003 | ^
004 | SyntaxError: invalid syntax
@grave rover :white_check_mark: Your eval job has completed with return code 0.
{'POP_TOP': 1, 'ROT_TWO': 2, 'ROT_THREE': 3, 'DUP_TOP': 4, 'DUP_TOP_TWO': 5, 'ROT_FOUR': 6, 'NOP': 9, 'UNARY_POSITIVE': 10, 'UNARY_NEGATIVE': 11, 'UNARY_NOT': 12, 'UNARY_INVERT': 15, 'BINARY_MATRIX_MULTIPLY': 16, 'INPLACE_MATRIX_MULTIPLY': 17, 'BINARY_POWER': 19, 'BINARY_MULTIPLY': 20, 'BINARY_MODULO': 22, 'BINARY_ADD': 23, 'BINARY_SUBTRACT': 24, 'BINARY_SUBSCR': 25, 'BINARY_FLOOR_DIVIDE': 26, 'BINARY_TRUE_DIVIDE': 27, 'INPLACE_FLOOR_DIVIDE': 28, 'INPLACE_TRUE_DIVIDE': 29, 'RERAISE': 48, 'WITH_EXCEPT_START': 49, 'GET_AITER': 50, 'GET_ANEXT': 51, 'BEFORE_ASYNC_WITH': 52, 'END_ASYNC_FOR': 54, 'INPLACE_ADD': 55, 'INPLACE_SUBTRACT': 56, 'INPLACE_MULTIPLY': 57, 'INPLACE_MODULO': 59, 'STORE_SUBSCR': 60, 'DELETE_SUBSCR': 61, 'BINARY_LSHIFT': 62, 'BINARY_RSHIFT': 63, 'BINARY_AND': 64, 'BINARY_XOR': 65, 'BINARY_OR': 66, 'INPLACE_POWER': 67, 'GET_ITER': 68, 'GET_YIELD_FROM_ITER': 69, 'PRINT_EXPR': 70, 'LOAD_BUILD_CLASS': 71, 'YIELD_FROM': 72, 'GET_AWAITABLE': 73, 'LOAD_ASSERTION_ERROR': 74, 'INP
... (truncated - too long)
Full output: https://paste.pythondiscord.com/ofotidaquj.txt?noredirect
well that was spooky
__import__('opcode').opmap==__import__('dis').opmap
make this false
"experimental python version" lol
asking #esoteric-python unironically for code advice
honestly at this point it's karma
seems to be a lot
+ PUSH_EXC_INFO = 35
+ POP_EXCEPT_AND_RERAISE = 37
+ BEFORE_WITH = 53
- POP_BLOCK = 87
- SETUP_FINALLY = 122
- LOAD_CLOSURE = 135
- LOAD_DEREF = 136
- STORE_DEREF = 137
- DELETE_DEREF = 138
+ MAKE_CELL = 135
+ LOAD_CLOSURE = 136
+ LOAD_DEREF = 137
+ STORE_DEREF = 138
+ DELETE_DEREF = 139
- SETUP_WITH = 143
- SETUP_ASYNC_WITH = 154
+ CALL_METHOD_KW = 166```
they changed opcodes ?
disregarding the opcode number, the changes I see:
Removed:
SETUP_WITH SETUP_FINALLY SETUP_ASYNC_WITH POP_BLOCK # Added:
PUSH_EXC_INFO POP_EXCEPT_AND_RERAISE MAKE_CELL CALL_METHOD_KW BEFORE_WITH
pretty horrendous 😵
thats using this snippet
__import__('opcode').opmap
makes me sad.. to think all the work I put into making unpyc work up to 3.9
alright I think I have the opcodes now
but here comes the painful part
how do jumps work now that we have 3-byte ops
we have what?
oh they're 4 bytes but with a dummy byte in between
I see
_unpack_opargs won't work with the new ops either 
oh well, pushing partial 3.11 support I guess
format is <op> <arg1> <dummy> <arg2>
which is kind of odd since you'd expect them to just use EXTENDED_ARG instead
oh wait no that's for size
JUMP_ABSOLUTE_QUICK=40 breaks the convention of HAVE_ARGUMENT=90
New adaptive interpreters in 3.11
They’re adaptive instructions:
There’s gonna be a counter and if inputs are expected it’s incremented otherwise it’s decremented, and once the counter hits the minimum value, the adaptive instruction is just replaced(?)
And so adaptive instructions are specialized at what their corresponding instruction is
yeah as you can see I looked into the 3.11 updates earlier
I didn't think they were merging that
there is one issue with this
the walrus operator doesn't work in comprehensions 😭
ice been forced to use map and filter as a result
/* Map from opcode to adaptive opcode.
Values of zero are ignored. */
static uint8_t adaptive_opcodes[256] = {
[LOAD_ATTR] = LOAD_ATTR_ADAPTIVE,
[LOAD_GLOBAL] = LOAD_GLOBAL_ADAPTIVE,
[LOAD_METHOD] = LOAD_METHOD_ADAPTIVE,
[BINARY_ADD] = BINARY_ADD_ADAPTIVE,
[BINARY_SUBSCR] = BINARY_SUBSCR_ADAPTIVE,
[STORE_ATTR] = STORE_ATTR_ADAPTIVE,
};
that's all in specialize.c, I guess they are merging it
wait really? How so?
!e
[(t := x) for x in range(5)]
@rapid sparrow :warning: Your eval job has completed with return code 0.
[No output]
oh it did work? o.O
!e py nums = [(x if (x:=y**2) > 10 else y) for y in range(100)] print(nums)
@grave rover :white_check_mark: Your eval job has completed with return code 0.
[0, 1, 2, 3, 16, 25, 36, 49, 64, 81, 100, 121, 144, 169, 196, 225, 256, 289, 324, 361, 400, 441, 484, 529, 576, 625, 676, 729, 784, 841, 900, 961, 1024, 1089, 1156, 1225, 1296, 1369, 1444, 1521, 1600, 1681, 1764, 1849, 1936, 2025, 2116, 2209, 2304, 2401, 2500, 2601, 2704, 2809, 2916, 3025, 3136, 3249, 3364, 3481, 3600, 3721, 3844, 3969, 4096, 4225, 4356, 4489, 4624, 4761, 4900, 5041, 5184, 5329, 5476, 5625, 5776, 5929, 6084, 6241, 6400, 6561, 6724, 6889, 7056, 7225, 7396, 7569, 7744, 7921, 8100, 8281, 8464, 8649, 8836, 9025, 9216, 9409, 9604, 9801]
@rapid sparrow :x: Your eval job has completed with return code 1.
001 | File "<string>", line 1
002 | list([*list([t := x]) for x in range(5)])
003 | ^
004 | SyntaxError: iterable unpacking cannot be used in comprehension
you can't unpack, but you can nest comps
you can also somewhat unpack if you know how
!e ```py
print([x
for y in [[1, 2],
[3, 4]]
for x in y])
@grave rover :white_check_mark: Your eval job has completed with return code 0.
[1, 2, 3, 4]
yeah that's what I meant
>>> import sys
>>> sys.setrecursionlimit(10)
>>> def fact(n, acc=1):
... if (n < 2):
... return acc
... return fact(n - 1, n * acc)
...
>>> fact(10)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 4, in fact
File "<stdin>", line 4, in fact
File "<stdin>", line 4, in fact
[Previous line repeated 5 more times]
File "<stdin>", line 2, in fact
RecursionError: maximum recursion depth exceeded in comparison
>>> dis.dis(fact)
2 0 LOAD_FAST 0 (n)
2 LOAD_CONST 1 (2)
4 COMPARE_OP 0 (<)
6 POP_JUMP_IF_FALSE 12
3 8 LOAD_FAST 1 (acc)
10 RETURN_VALUE
4 >> 12 LOAD_GLOBAL 0 (fact)
14 LOAD_FAST 0 (n)
16 LOAD_CONST 2 (1)
18 BINARY_SUBTRACT
20 LOAD_FAST 0 (n)
22 LOAD_FAST 1 (acc)
24 BINARY_MULTIPLY
26 CALL_FUNCTION 2
28 RETURN_VALUE
>>> fact = tco(fact)
>>> dis.dis(fact)
2 >> 0 LOAD_FAST 0 (n)
2 LOAD_CONST 1 (2)
4 COMPARE_OP 0 (<)
6 POP_JUMP_IF_FALSE 12
3 8 LOAD_FAST 1 (acc)
10 RETURN_VALUE
4 >> 12 NOP
14 LOAD_FAST 0 (n)
16 LOAD_CONST 2 (1)
18 BINARY_SUBTRACT
20 LOAD_FAST 0 (n)
22 LOAD_FAST 1 (acc)
24 BINARY_MULTIPLY
26 STORE_FAST 1 (acc)
28 STORE_FAST 0 (n)
30 JUMP_ABSOLUTE 0
>>> fact(10)
3628800
>>> ```
@grave rover had to handroll a smaller dissassembler but its working
nice
I'm working on actually testing stuff on 3.7
but uh
ValueError: code: varnames is too small
what raises this
/* Ensure that the co_varnames has enough names to cover the arg counts.
* Note that totalargs = nlocals - nplainlocals. We check nplainlocals
* here to avoid the possibility of overflow (however remote). */
int nlocals;
get_localsplus_counts(con->localsplusnames, con->localspluskinds,
&nlocals, NULL, NULL, NULL);
int nplainlocals = nlocals -
con->argcount -
con->kwonlyargcount -
((con->flags & CO_VARARGS) != 0) -
((con->flags & CO_VARKEYWORDS) != 0);
if (nplainlocals < 0) {
PyErr_SetString(PyExc_ValueError, "code: co_varnames is too small");
return -1;
}
not quite
the weird thing is that my nlocals, argcount, kwonlyargcount and varnames are all the same
nlocals - argcount - kwonlyargcount should be >=0
nice
and len(varnames) == nlocals
i hacked up a package to use the docstring to figure out what to pass to the CodeType ctor 😅
oh yea i wrote one of those too lol
otherwise i don't see how it can work on more than a couple versions
haha, sweet
matches at runtime (3, 3, 0)
why doesn't it have a __text_signature__ ...
def replace(codeobj, **kwargs):
code_args = []
argnames = type(codeobj).__doc__.split("(")[1].split("[")[0].split(",")
for argname in argnames:
argname = argname.strip()
if argname == "codestring":
argname = "code"
if argname == "constants":
argname = "consts"
code_args.append(kwargs.get("co_"+argname, getattr(codeobj, "co_"+argname)))
return type(codeobj)(*code_args)``` this is I think a working version
that's literally my exact code
@grave rover maybe flags issue?
I ended up doing it like this ```py
def code_replace(code_obj: CodeType, **kwargs) -> CodeType:
if sys.version_info >= (3, 8):
return code_obj.replace(**kwargs)
def _(n: str):
return kwargs.get(n, getattr(code_obj, n))
print(_("co_nlocals"), _("co_argcount"), _("co_kwonlyargcount"), _("co_flags"))
return CodeType(
_("co_argcount"),
_("co_kwonlyargcount"),
_("co_nlocals"),
_("co_stacksize"),
_("co_stacksize"),
_("co_code"),
_("co_consts"),
_("co_names"),
_("co_varnames"),
_("co_filename"),
_("co_name"),
_("co_firstlineno"),
_("co_lnotab"),
_("co_freevars"),
_("co_cellvars")
)
flags are the same on both too (67)
nice
ye
there we go
now there's like kwposonlyonlyonlyargcount
it was passing stack as flags
@rugged sparrow <3.8 support should be up now
i ran into that label bug earlier on 3.9
yup fixed that too
i bet there's hundreds of these
elif op in hasjabs:
idx = arg * 2 if sys.version_info >= (3, 10) else arg
looks like they changed in 3.10
makes sense since it'd always be a multiple of 2
wait jumps are now index/2?
wait that means I shouldn't divide by 2 when writing again
for absjumps yes
reljumps not sure
there's no stability guarantees for cpython bytecode i guess
python 3.12 - "make bytecode it big-endian"
if you find any issues with the version I just pushed due to jumps let me know @rugged sparrow
will do
import dis
def decompile(byc):
jmp_tbl = {}
decomp = []
idx = 0
while idx < len(byc):
op, arg = byc[idx: idx + 2]
idx += 2
while dis.opname[op] == 'EXTENDED_ARG':
oarg = arg
op, arg = byc[idx: idx + 2]
arg |= oarg << 8
idx += 2
decomp.append([dis.opname[op], arg])
for idx, ((op, arg), lst) in enumerate(zip(decomp, decomp)):
if op == 'JUMP_FORWARD':
jmp_tbl[id(lst)] = decomp[idx + (arg // 2) + 1]
elif 'JUMP' in op:
jmp_tbl[id(lst)] = decomp[arg // 2]
return decomp, jmp_tbl
def insert_extensions(decomp):
while any(filter(lambda t:t[1] > 255, decomp)):
for idx, (op, arg) in enumerate(decomp[:]):
nshift = (arg.bit_length() / 8).__ceil__()
if op != 'JUMP_FORWARD' and 'JUMP' in op and nshift:
arg += (nshift - 1) * 2
if arg > 255:
decomp[idx][1] = arg & 255
decomp.insert(idx, ['EXTENDED_ARG', arg >> 8])
break
def find(decomp, dest):
for idx, lst in enumerate(decomp):
if lst is dest:
return idx
def recompile(decomp, jmp_tbl):
insert_extensions(decomp) # insert EXTENDED_ARG for non JUMPs
for idx, ((op, arg), lst) in enumerate(zip(decomp, decomp)):
if 'JUMP' in op:
offset = (idx + 1) if op == 'JUMP_FORWARD' else 0
dest_op = jmp_tbl.get(id(lst))
if (dest := find(decomp, dest_op)) is not None:
arg = lst[1] = (dest - offset) * 2
insert_extensions(decomp) # insert EXTENDED_ARG for JUMPs
return b''.join(bytes([dis.opmap[op], arg]) for op, arg in decomp)``` this is my small decompiler im using rn
it prob has some issues but it works for 3.9
that seems incredibly painful lol
ohhh
what kind of output do you get with it
a list of [opname, oparg] and a jump table
oh that's cool, that should be in the dis module
it collapses EXTENDED_ARGS and then re-expands them during recompilation
there's a pyasm i was trying to get to work a little better to parse the typical listing, but haven't had much success
its not too bad, it just uses a jump table instead of storing labels in the decompilation (also i wrote it at 1 am so the code quality is questionable)
/me steals it
go ahead just credit me
define decompiler
if it's turning bytecode into ops then I'm pretty happy with mine
but into actual code there's decompyle6
preferably generate python source
I think decompyle6 is about the only tool there is for that atm
if it's readable that's not a hard requirement
it's unmaintained now though, right?
sounds like a fun task to do though, might take that up later
this is ny attempt to update 'unpyc' : https://github.com/greyblue9/unpyc37-3.10/
you and i have different definitions of "fun task" lmao
sometimes it works great, sometimes it fails with "popped an empty stack!"
ah
tried to fix the cases i could track down
using stack_effect?
i'm trying to remember.. it's hard to find where things went off the rails mostly
i don't think i've used stack_effect
strange, I always remember at what point I gave up :P
there's enough errors that old memories get replaced by new ones
haha
i do like uncompyle6 too
the main github has a note that the guy was gonna rewrite it
that's been like that for years
so how would you use stack_effect
don't you need to know exactly what to do with each pop/push?
not 100%
sure if I see LOAD_GLOBAL("input") CALL_FUNCTION(0) I need to understand it calls input()
but I don't actively track the stack effect for that
the generics thing looks cool. you added type arguments for List, Tuple, etc that actually work?
til there's two libraries named pyasm
that aren't at all similar
very nice
i'm thinking if the python
parser can be generated, it should be possible to generate a de-parser. is this right, or just crazy talk
if compilers were injective functions then yes
you can think of compiling as lossy compression. You have enough data after to run the code but you lose a lot of structural information
What combinations of compiling / obfuscation tools do you guys use to create code that is virtually impossible to get human-readable stuff out of reverse engineering?
I already use Cython for compiling but I'd like to be able to generate a single executable that isn't easy to decompile, or at least not easy to read when decompiled
hard to do honestly, since a lot of cpython internals are well-defined
It's worth noting that obfuscation doesn't really stop dedicated attackers, so don't use it for security purposes.
The obfuscation is less about security and more about making it difficult to read
ohh, that's definitely true with C/C++ compiler
rewrite in C/C++ or go full Cython i guess
Thing with Cython is that it's still possible to reverse engineer
c/c++ is also possible to reverse engineer
no obfuscation will work against a sufficiently determined actor
So perhaps you don't need to bother
why would that be though, are there secret keys in it ?
why would you want it to be difficult to read, I mean . just curious
I just want to make it as difficult as possible for someone who wants to reverse engineer my code
I'm doing something stupid like storing any secret/private keys or passwords, I just want to make it not worth the effort for a bad actor to try to look at the source code
that's what I'd like to understand more. it's not to hide the logic, you just want to make things difficult for would-be reverse-engineering?
what if they're not a bad actor, they like your software, and wanted to see how you did it
the way I see it, it's like spraypainting over a museum painting
I suppose it is like that
also those not-bad actors can also help improve your program
The thing with one of my apps is that I have some stuff that I want to be open source, and other stuff that I want to be closed - that closed stuff is what I'd want to obfuscate
!e Hmm...
print(bool(type(None)))
print(type(None).__bool__(None))
@prisma coral :white_check_mark: Your eval job has completed with return code 0.
001 | True
002 | False
Welp I guess instead of True and False I'll just use these from now on
I mean those are effectively bool(NoneType) and bool(None) respectively
Python truthy values like if [0]: pass
ah yes I see it now. ty!
!e
print(
f"{not ()=} \n"
f"{not ().__class__=}"
)
``` coincidence?
@rapid sparrow :white_check_mark: Your eval job has completed with return code 0.
001 | not ()=True
002 | not ().__class__=False
bonus points if someone knows how to make a metaclass that evaluates False
like class a(metaclass=...); a is False?
thinking like
class a(metaclass=...)
bool(a.__class__) # False
i don't even know where the truth value of a type object comes from. Is it just because bool(not None) -> True ?
oh no I know of several ctype hacks to do this
** the metaclass part is optional
AttributeError: type object 'type' has no attribute '__bool__'
!e ```py
class M(type):
bool = lambda s:False
class A(type, metaclass=M):pass
class a(metaclass=A):pass
print(bool(a.class))```
@rugged sparrow :white_check_mark: Your eval job has completed with return code 0.
False
w a t
!e
class M(type):
__bool__ = lambda s:False
class A(type, metaclass=M):pass
class a(metaclass=A):pass
an_a = a()
print(bool(an_a.__class__))
@rapid sparrow :white_check_mark: Your eval job has completed with return code 0.
True
asdfghjkl
bonus awarded
but I'm gonna try to do a less nice version
hello id(type)
ctypes time
what did you try
so you tried to read the reference counter of type as a pointer to 16 characters?
yes, and instead it tried to read from address 0x3d >_>
thats the current ref count of type most likely
whats your goal?
i just wanted it as character data
read the first 16 bytes of type?
yea
then (c_char * 16).from_address(id(type))
omg it worked, thank you so much ❤️
does from_addtess mean I was creating a pointer to the thing I wanted?
>>> arr.raw
b'=\x00\x00\x00\x00\x00\x00\x00P\xb1\xf9\xf7\x7f\x00\x00\x00'
``` is what I was looking for
0x3b is the = iirc
that must be refcount, cause the rest are all zeroes, like you said..
typedef struct _object {
Py_ssize_t ob_refcnt;
PyTypeObject *ob_type;
} PyObject;
for a PyTypeObject it's ```c
typedef struct _PyTypeObject PyTypeObject;
struct _PyTypeObject {
Py_ssize_t ob_refcnt;
PyTypeObject ob_type;
Py_ssize_t ob_size;
const char tp_name;
Py_ssize_t tp_basicsize;
Py_ssize_t tp_itemsize;
void* tp_dealloc;
// ...
}```
so it's definitely ob_refcnt then
how'd you get that? did you macro expand it?
normally it will say e.g. PY_OBJECT_HEAD or somesuch
was just curious if there was a technique i don't know of
oh it's defined to empty is why
cool
what is this magic
important to note PyVarObject is NOT PyObject
what's the generated bytecode?
I'm guessing OPTIONALOP works like DUP_TOP() LOAD_CONST(None) COMPARE_OP(is not) POP_JUMP_IF_FALSE(+2)?
idk what you mean by that lol
that seems
wrong
because then a?.b.c.d is handled as if it were a?.b?.c?.d
wouldn't you instead just want ```c
TARGET(OPTIONALOP): {
if (TOP() == Py_None) {
next_instr++;
}
DISPATCH();
}
also ```
10 LOAD_CONST 0 ('')
12 COALESCE 0
e.g. a?.b ?? new_b() will always call new_b
huh
I assumed it would since LOAD_CONST('') was before the COALESCE so it'd be evaluated first
well yeah but LOAD_FAST(a) would still happen before BINARY_POWER
oh sweet
which repo os it in?
I have a weird reloading-related (I already am in "don't do that" territory thus asking this chanmrl) issue with supposedly incompatible types in a super() that's rejecting some arguments (I think..)
What would explain why I get:
Traceback
File <stdin>, line 1, in <module>
pythonrc.exec_in_module(readstring('/data/media/0/,scripts/edited--gdb_extras.py.1630903616.bak'), gdb_extras):
File /data/media/0/pythonrc.py, line 3615, in exec_in_module
result = builtin_exec(codeobj, dicts[0], _out_locals):
File /tm/tmp_py_src_7tppmsbs.py, line 757, in <module>
if IS_GDB: ListExCommand():
File /tm/tmp_py_src_7tppmsbs.py, line 470, in __init__
super(ListExCommand, self).__init__("li", gdb.COMMAND_USER):
TypeError: super(type, obj): obj must be an instance or subtype of type
BUT I went to check the two names there (ListExCommand and self) from the traceback frame, to see if they were incompatible evil twins:
>>> pp([(id(x),x) for x in (self.__class__, ListExCommand,)])
[(387405958144, <class 'gdb_extras.ListExCommand'>),
(387405958144, <class 'gdb_extras.ListExCommand'>)]
``` bit confused..
oh and the usecase is a source lister that expands macros that you can still step through, if anyone was curious
?
!e
from ctypes import c_void_p
class Rick:
def __str__(self):
return "Never gonna make you cry"
c_void_p.from_address(id(object)+8).value = id(Rick)
Never_gonna_run_around = desert_you = object
print(Never_gonna_run_around and desert_you)
@knotty delta :white_check_mark: Your eval job has completed with return code 0.
Never gonna make you cry
i meant to reply to @sick hound , sorry.
It tortures it 👀
set it's ob_type to Rick? haha
pretty amazing the interpreter manages to even exit successfully
@sick hound is your None-coalescing operator fork on github?
can someone explain the inner workings of "import this"?
you mean the cipher?
it's just ROT13
the "Ceasar Cipher"
for every letter, it "advances" 13 letters ahead, wrapping around at the end of the alphabet.
it's a module that contains a printer object basically so that when you import it, text gets printed out
from what I recall
ok, it's simpler than that. It just prints. But yeah, I guess you were asking about the ciphered text so nevermind ...
yea the cipher is confusing
the this module is curious
I like it
looking at it:
>>> this.c
97
>>> this.d
{'A': 'N', 'B': 'O', 'C': 'P', 'D': 'Q', 'E': 'R', 'F': 'S', 'G': 'T', 'H': 'U', 'I': 'V', 'J': 'W', 'K': 'X', 'L': 'Y', 'M': 'Z', 'N': 'A', 'O': 'B', 'P': 'C', 'Q': 'D', 'R': 'E', 'S': 'F', 'T': 'G', 'U': 'H', 'V': 'I', 'W': 'J', 'X': 'K', 'Y': 'L', 'Z': 'M', 'a': 'n', 'b': 'o', 'c': 'p', 'd': 'q', 'e': 'r', 'f': 's', 'g': 't', 'h': 'u', 'i': 'v', 'j': 'w', 'k': 'x', 'l': 'y', 'm': 'z', 'n': 'a', 'o': 'b', 'p': 'c', 'q': 'd', 'r': 'e', 's': 'f', 't': 'g', 'u': 'h', 'v': 'i', 'w': 'j', 'x': 'k', 'y': 'l', 'z': 'm'}
>>> this.i
25
>>> this.s
"Gur Mra bs Clguba, ol Gvz Crgref\n\nOrnhgvshy vf orggre guna htyl.\nRkcyvpvg vf orggre guna vzcyvpvg.\nFvzcyr vf orggre guna pbzcyrk.\nPbzcyrk vf orggre guna pbzcyvpngrq.\nSyng vf orggre guna arfgrq.\nFcnefr vf orggre guna qrafr.\nErnqnovyvgl pbhagf.\nFcrpvny pnfrf nera'g fcrpvny rabhtu gb oernx gur ehyrf.\nNygubhtu cenpgvpnyvgl orngf chevgl.\nReebef fubhyq arire cnff fvyragyl.\nHayrff rkcyvpvgyl fvyraprq.\nVa gur snpr bs nzovthvgl, ershfr gur grzcgngvba gb thrff.\nGurer fubhyq or bar-- naq cersrenoyl bayl bar --boivbhf jnl gb qb vg.\nNygubhtu gung jnl znl abg or boivbhf ng svefg hayrff lbh'er Qhgpu.\nAbj vf orggre guna arire.\nNygubhtu arire vf bsgra orggre guna *evtug* abj.\nVs gur vzcyrzragngvba vf uneq gb rkcynva, vg'f n onq vqrn.\nVs gur vzcyrzragngvba vf rnfl gb rkcynva, vg znl or n tbbq vqrn.\nAnzrfcnprf ner bar ubaxvat terng vqrn -- yrg'f qb zber bs gubfr!"
!e
print(chr(97))
#this.d is a rot13 dict
i=25#length of the alphabet?
#this.s is just the string
@golden finch :white_check_mark: Your eval job has completed with return code 0.
a
actually, I bet it's the difference between lower and upper case
s = """Gur Mra bs Clguba, ol Gvz Crgref
Ornhgvshy vf orggre guna htyl.
Rkcyvpvg vf orggre guna vzcyvpvg.
Fvzcyr vf orggre guna pbzcyrk.
Pbzcyrk vf orggre guna pbzcyvpngrq.
Syng vf orggre guna arfgrq.
Fcnefr vf orggre guna qrafr.
Ernqnovyvgl pbhagf.
Fcrpvny pnfrf nera'g fcrpvny rabhtu gb oernx gur ehyrf.
Nygubhtu cenpgvpnyvgl orngf chevgl.
Reebef fubhyq arire cnff fvyragyl.
Hayrff rkcyvpvgyl fvyraprq.
Va gur snpr bs nzovthvgl, ershfr gur grzcgngvba gb thrff.
Gurer fubhyq or bar-- naq cersrenoyl bayl bar --boivbhf jnl gb qb vg.
Nygubhtu gung jnl znl abg or boivbhf ng svefg hayrff lbh'er Qhgpu.
Abj vf orggre guna arire.
Nygubhtu arire vf bsgra orggre guna *evtug* abj.
Vs gur vzcyrzragngvba vf uneq gb rkcynva, vg'f n onq vqrn.
Vs gur vzcyrzragngvba vf rnfl gb rkcynva, vg znl or n tbbq vqrn.
Anzrfcnprf ner bar ubaxvat terng vqrn -- yrg'f qb zber bs gubfr!"""
d = {}
for c in (65, 97):
for i in range(26):
d[chr(i+c)] = chr((i+13) % 26 + c)
print("".join([d.get(c, c) for c in s]))
complete source
looks like the two for loops generate d
thats awesome
are you actually turning __code__ into code? madlad
made a small fix to make strings work
oh no worries 👍
just don't lose or FUBAR it by accident (for me the line is very thin between fixing and FUBAR'ing)
n = 0
if (2 * offset + n < len(self.code)):
op = self.code[2 * offset + n:None]
arg, op = unpack('BB', op[None:2])
n = n += 1
if (op >= 90):
else:
current = stack_effect(op, arg) += stack_effect(op)
self.max = max(self.max, current)
if (op in hasbranch):
new_jumped = jumped + [arg, current + n]
if (arg not in jumped):
self.check_offset(arg, current, new_jumped)
if (current + n not in jumped):
self.check_offset(current + n, current, new_jumped)
return
if (op in hasjabs):
if (arg not in jumped):
self.check_offset(arg, current, jumped + [arg])
return
if (op in hasjrel):
if (offset + n + arg not in jumped):
self.check_offset(offset + n + arg, current, jumped + [offset + n + arg])
return
if (op == opmap['RETURN_VALUE']):
return
not the worst result
source that caused that is ```py
def check_offset(self, offset: int, current: int, jumped: list):
n = 0
while 2 * (offset + n) < len(self.code):
op = self.code[2 * (offset + n):]
op, arg = unpack("BB", op[:2])
n += 1
current += stack_effect(op, arg) if op >= 90 else stack_effect(op)
self.max = max(self.max, current)
# TODO: Something still goes wrong here
# if current < 0:
# raise ValueError("Negative stack index!")
if op in hasbranch:
new_jumped = jumped + [arg, current + n]
# do both branches
if arg not in jumped:
self.check_offset(arg, current, new_jumped)
if current + n not in jumped:
self.check_offset(current + n, current, new_jumped)
return
elif op in hasjabs and arg not in jumped:
# do jump
self.check_offset(arg, current, jumped + [arg])
return
elif op in hasjrel and offset + n + arg not in jumped:
# do jump
self.check_offset(offset + n + arg, current, jumped + [offset + n + arg])
return
if op == opmap["RETURN_VALUE"]:
break
that x if y else z really messes it up
LOAD_FAST(id=124, arg='current'),
LOAD_FAST(id=124, arg='op'),
LOAD_CONST(id=100, arg=90),
COMPARE_OP(id=107, arg='>='),
POP_JUMP_IF_FALSE(id=114, arg=100),
LOAD_GLOBAL(id=116, arg='stack_effect'),
LOAD_FAST(id=124, arg='op'),
LOAD_FAST(id=124, arg='arg'),
CALL_FUNCTION(id=131, arg=2),
JUMP_FORWARD(id=110, arg=104),
LOAD_GLOBAL(id=116, arg='stack_effect'),
LOAD_FAST(id=124, arg='op'),
CALL_FUNCTION(id=131, arg=1),
INPLACE_ADD(id=55, arg=0),
STORE_FAST(id=125, arg='current'),
I wonder if there's an easy way to detect this structure
maybe check if the stack is empty?
but there's probably other situations
idk
n = 0
if (2 * offset + n < len(self.code)):
op = self.code[2 * offset + n:]
arg, op = unpack('BB', op[:2])
n += 1
stack_effect(op, arg) if (op >= 90) else current += stack_effect(op)
self.max = max(self.max, current)
if (op in hasbranch):
new_jumped = jumped + [arg, current + n]
if (arg not in jumped):
self.check_offset(arg, current, new_jumped)
if (current + n not in jumped):
self.check_offset(current + n, current, new_jumped)
return
if (op in hasjabs):
if (arg not in jumped):
self.check_offset(arg, current, jumped + [arg])
return
if (op in hasjrel):
if (offset + n + arg not in jumped):
self.check_offset(offset + n + arg, current, jumped + [offset + n + arg])
return
if (op == opmap['RETURN_VALUE']):
break
continue
return
``` if only that `break` lined up better...
really sweet looking
are you making a disassembler?
This doesn't help you does it?
^ the traceback seems like it could match
sys is the first module loaded.. you could set a bteakpoint on PyException_SetTraceback and enable PYTHONVERBOSE maybe
oh, oh, maybe you need re-generate the frozen modules ??
you would need a debugger. did you build with MSVC?
ok, thst might workk
get WinDbg
from MS store
the new one doesn't suck
then you will be able to source debug and set breakpoints
this explains the UI - i actually think its easier ith WinDbg than VSCode https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/debugging-using-windbg-preview
gdb won't work with MSVC -compiled binaries, iirc
other last resort action is nuke (just) the build dir and rebuild python
did you build in the source dir? or separate
also delete any and all __pycache__ dirs
thats only the win-specific parts, right?
you see .obj files under Python/ ?
can you locate sysmodule.o or sysmodule.obj
maybe try build.bat -r --regen
alternately if that fails, delete everything under PCbuild/obj
that's the "build dir" i suppose
and this:
%MSBUILD% "%dir%\pythoncore.vcxproj" /t:Regen %verbose%^ /p:Configuration=%conf% /p:Platform=%platf%^ /p:ForceRegen=true
"will regenerate all grammar, tokens, and opcodes."
🤷♂️
ah
one thing I would try
is to change where it says sys. to __import__("sys"). - and see if it can maybe get passed initializing builtins then
in Lib/importlib/_bootstrap.py:255
does running python with python.exe -S -s -x start?
.topic
Suggest more topics here!
wow there are actually topics for esoteric python
wow, that shouldn't be happening. what's the code for _requires_builtin_wrapper now?
the bytecode is messed up
it should of course have LOAD_DEREF
might be worth setting PYTHONDONTWRITEBYTECODE=1 in the environment until you're out of the woods
Splitting code into blocks is actually painful what the heck
For some reason it keeps trying to recurse even when I'm only allowing forward junps
Do you have handling for while loops?
not yet
https://hastebin.com/tazokejuje.py this is that same code converted into blocks
I need to clean it up lol
installing tools for one thing
What’s the best tutorial on ctypes out there?
Find a good C tutorial
depends
if you're looking to use it for a "sanctioned" purpose, or more "esoteric" purposes
this one is good though: https://yizhang82.dev/python-interop-ctypes
https://yizhang82.dev/python-interop-capi
https://yizhang82.dev/python-interop-inside-ctypes (skip to part 3 if you're bored by parts 1 and 2)
Calling C functions from Python - part 1 - using ctypes
Calling C functions from Python - part 2 - writing CPython extensions using Python/C API
Calling C functions from Python - part 3 - deep dive into ctypes implementation in CPython
The latter
oh wrong reply
and thanks :D
thought so
How do you all know so much about the byte offset of objects? Like I'm wondering how you all know that id(True) + 8 defines is behavior
I'd() always gives you the memory address
Well not quite, CPython does so, but PyPy doesn't and it's not guaranteed in general.
From memory, the id plus 8 is where actual object starts. The eight bytes are some sort of header. The object is stored as a pointer, and if two pointers are equal to each other, then they point to the same object. That's who Python checks is.
ohh so True is changed to something else
id(object) is the address that the object starts at. The first 8 bytes are the reference count, the second 8 are a pointer to the type of the object object.__class__
When python does is it is equivalent to id(obj1) == id(obj2)
This code exchanges the base type of object from type to Rick. This means that object.__repr__()calls Rick.__repr__(object)
It is, it just compares the pointers
I was saying that's the equivalent python
does anyone know what PyInterpreterState_Clear is useful for?
Good to now there is also an 'esoteric' version of Python. But why? 🤣
But the esoteric languages are just for the fun of it right? not really useful
👍 thank you for your answer
🐸 obfuscated javascript equals unveiled C
JS obfuscation isn't really that, but it's a side effect. The intent is to make the file size as small as possible, by removing all whitespace, comments, swapping to equivalent smaller code, renaming variables to one-letter versions, and also generally optimising by inlining constants etc.
3.10 has broken a lot of my bytecode level hacks
Same
There's a lot of bytecode changes
are they all documented anywhere?
Hello
For which purpose you are relying on bytecodes
Various runtime shenanigans
Why nowadays people are using python for microcontroller programming and flashing converted Cpython code into controller ?
because it's funny
What changed with 3.10's bytecode that breaks older bytecode hacks?
There are some improved optimizations (early returns being one of them) so my scanner functions that look for certain patterns don't account for them
Is there any less information than before, or is it just a matter of fixing all of the broken bits?
I need to make some portions more general. Tbh might just make a generic bytecode scanner that can look for defined patterns
Well actually the adaptive bytecode will be deactivated if you enable tracing etc, so you shouldn’t have to deal with them.
I might be able to do some cool things with the adaptive bytecode tho
Ah
It’s also not visible in the code object, it first copies the byte code to a private array before modifying.
!e range(3, 4, s=5, g=7, *a, **b, **c)
@thin trout :x: Your eval job has completed with return code 1.
001 | Traceback (most recent call last):
002 | File "<string>", line 1, in <module>
003 | NameError: name 'a' is not defined
I thought that'd be invalid syntax
Why would it? It's just normal call syntax
Hello you crazy golfers ⛳
anyone have any written guides for golfing if statements?
wanted to golf this a bit and learn about golfing and stress out my university lecturer.
Depends what you're doing, maybe send an example
ok
def windspeed(inputint):
if inputint < 20:
print("Breeze")
elif 20 < inputint < 50:
print("Gale")
elif inputint > 50:
print("Storm")
positional arguments after keyword arguments
!e
range(3, 4, s=5, g=7, *(1, 2))
@eager sphinx :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: range() takes no keyword arguments
def f(*args, **kwargs):
...
f(3, 4, s=5, g=7, *(1, 2))
!e ```python
def windspeed(inputint):
if inputint < 20:
print("Breeze")
elif 20 < inputint < 50:
print("Gale")
elif inputint > 50:
print("Storm")
@tall glacier :warning: Your eval job has completed with return code 0.
[No output]
!e
def f(*args, **kwargs):
...
f(3, 4, s=5, g=7, *(1, 2))
@eager sphinx :warning: Your eval job has completed with return code 0.
[No output]
thats pretty cool
Very weird
*args seems to be fine with it
!e
windspeed=lambda _:print("Breeze")if _<20 else print("Gale")if 20<_<50 else print("Storm")
windspeed(12)
windspeed(34)
windspeed(64)
@gritty mesa :white_check_mark: Your eval job has completed with return code 0.
001 | Breeze
002 | Gale
003 | Storm
Nice thank you jack, just one question whats the _?
Just a variable name that's terrible to confuse things more
You'll see that a lot around here
I have noticed, however it like a race to get chars down?
Tight
I want to get into code golfing but I feel like I am not that good at programming yet lol
thank you kind internet people for humoring my questions 🙂 @gritty mesa && @sick hound 🙂
!e I'm sure your lecturer would have fun with this
from __future__ import annotations
@lambda c:c()
class __annotations__:
def __setitem__(self, k, v):
if k == "For":
self.condition = v.split(",")[1].strip()
self.var = v.split(",")[0].strip("(")
globals()[self.var] = 0
elif k == "_":
code = v.strip("{").strip("}").strip().split(", ")
code_string = "\n".join(stuff.strip("'") for stuff in code)
while eval(self.condition):
exec(code_string)
globals()[self.var] += 1
For:(x, x < 5, ++x);_:{
print(x)
}
@gritty mesa :white_check_mark: Your eval job has completed with return code 0.
001 | 0
002 | 1
003 | 2
004 | 3
005 | 4
my eyes o.0
@sick hound :white_check_mark: Your eval job has completed with return code 0.
001 | 0
002 | 1
003 | 2
004 | 3
005 | 4
006 | 5
007 | 6
008 | __
009 | 21
import __future__ as __past__ good lord
@sick hound :white_check_mark: Your eval job has completed with return code 0.
001 | 0
002 | 1
003 | 2
004 | 3
005 | 4
006 | 5
007 | 6
008 | __
009 | 21
]e Will this work first try, here we go
exec('def windspeed(_):\n\tmatch _:\n\t\tcase _<20:print("Breeze")\n\t\tcase 20<_<50:print("Gale")\n\t\tcase _>50:print("Storm")\nwindspeed(5)')
@gritty mesa :x: Your eval job has completed with return code 1.
001 | Traceback (most recent call last):
002 | File "<string>", line 1, in <module>
003 | File "<string>", line 3
004 | case _<20:print("Breeze")
005 | ^
006 | SyntaxError: expected ':'
windspeed=lambda i:print([["Gale","Storm"][i>50],"Breeze"][i<20])
```slightly different, but illustrates the idea well
you can't do this with case
print('def windspeed(_):\n\tmatch _:\n\t\tcase _<20:print("Breeze")\n\t\tcase 20<_<50:print("Gale")\n\t\tcase _>50:print("Storm")\nwindspeed(5)')
!e
print('def windspeed(_):\n\tmatch _:\n\t\tcase _<20:print("Breeze")\n\t\tcase 20<_<50:print("Gale")\n\t\tcase _>50:print("Storm")\nwindspeed(5)')
@gritty mesa :white_check_mark: Your eval job has completed with return code 0.
001 | def windspeed(_):
002 | match _:
003 | case _<20:print("Breeze")
004 | case 20<_<50:print("Gale")
005 | case _>50:print("Storm")
006 | windspeed(5)
it would be case _ if _ < 20 IIRC
oh I was looking at this from the codeingame guide
!e Maybeeee
exec('def windspeed(i):\n\tmatch i:\n\t\tcase i<20:print("Breeze")\n\t\tcase 20<i<50:print("Gale")\n\t\tcase i>50:print("Storm")\nwindspeed(5)')
@gritty mesa :x: Your eval job has completed with return code 1.
001 | Traceback (most recent call last):
002 | File "<string>", line 1, in <module>
003 | File "<string>", line 2
004 | match i:
005 | ^
006 | SyntaxError: invalid syntax
damn
!e
print('def windspeed(i):\n\tmatch i:\n\t\tcase i<20:print("Breeze")\n\t\tcase 20<i<50:print("Gale")\n\t\tcase i>50:print("Storm")\nwindspeed(5)')
@gritty mesa :white_check_mark: Your eval job has completed with return code 0.
001 | def windspeed(i):
002 | match i:
003 | case i<20:print("Breeze")
004 | case 20<i<50:print("Gale")
005 | case i>50:print("Storm")
006 | windspeed(5)
Why are you invalid, I need to read up on patma
oh
wait
]e
exec('def windspeed(i):\n\tmatch i:\n\t\tcase i<20:print("Breeze")\n\t\tcase 20<i<50:print("Gale")\n\t\tcase i>50:print("Storm")\nwindspeed(5)')
@gritty mesa :x: Your eval job has completed with return code 1.
001 | Traceback (most recent call last):
002 | File "<string>", line 1, in <module>
003 | File "<string>", line 3
004 | case i<20:print("Breeze")
005 | ^
006 | SyntaxError: expected ':'
Ok yeah might have to be what you said lak
I need to go have a read of the patma pep at some point
Shouldn't print anything for 20, 50
!e ```py
windspeed=lambda i:print([["Gale","Storm"][i>50],"Breeze"][i<20])
def windspeed_og(inputint):
if inputint < 20:
print("Breeze")
elif 20 < inputint < 50:
print("Gale")
elif inputint > 50:
print("Storm")
print("Lambda:")
windspeed(50)
windspeed(20)
print("Og:")
windspeed_og(50)
windspeed_og(20)
@tacit cobalt :white_check_mark: Your eval job has completed with return code 0.
001 | Lambda:
002 | Gale
003 | Gale
004 | Og:
Although I guess that is more an issue with the original
yeah, you can work around that, but it would get longer
but the [a,b][cond] is a common way to golf these things
Yeah it is nice
is there a way to set a class only variable after class creation?
everything i try leads to them being class and instance variables
!e py print(str().join(map(chr,[x:=-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~0,_:=-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~x,*(o:=-~-~-~-~-~-~-~_,o),k:=-~-~-~o,*[t:=-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~0,-~-~-~-~-~-~-~-~-~-~-~-~t,][::~0],-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~x,k,-~-~-~k,o,--~-~-~-~-~-~-~-~-o,-~t])))
@cerulean rivet :white_check_mark: Your eval job has completed with return code 0.
Hello

Hey, I'm back
How can I change the values of strings in shell?
>>> 'foo'
'foo'
>>> #magic here (ctypes?)
>>> 'foo'
'bar'
doing it in script is trivial (just modify co.co_consts)
probably worth pinging our resident maestro-geniuses, @rugged sparrow and @sick hound
You can theoretically modify the interned strings table
now how is this structured and how would I go about it?
It's an internal dictionary object. Once you get it it's fairly simple, but getting a reference is tricky
this class variable thing is trickier than i expected
and im completely stumped
i think it requires adding either a getattribute hook to the metaclass or property usage on the metaclass
You have to hook internal C functions
asm_hook.py can do it ( and has commented code that can get the table)
pysnippets on my GitHub
How do you use memset?
@sick hound :white_check_mark: Your eval job has completed with return code 0.
bar
mhm
now how would I do this?
okay thanks
this?
Yea look at the commented out code
Just uncomment the commented code then do from asm_hook import interned
Should work
thanks
what is POP_BLOCK insturction? when is it used?
I know blocks are for try/except kinds of things, but docs say "...must be an exception handler block" and "denoting try statements, and such" implying there are other uses too
how does python JUMP to an index above 255?
for example, there is a bytecode of length 400.
how do I JUMP_ABSOLUTE to 300?
yeah it's a bit unclear.
so far I only found the use of it in try and with statement.
@sick hound :x: Your eval job has completed with return code 1.
001 | File "<string>", line 1
002 | ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( print to output: too many nested parentheses (with type SyntaxError) and (import stack).traceback << in "<string>" file: line 1 && (import exception.pointer) && exception.pointer << (import compiler).first_instrblock() )))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))
003 | ^
004 | SyntaxError: too many nested parentheses
Hey, I just ran into the syntax error I mentioned a while back, but couldn't remember exactly what it was
i had said the walrus operator couldn't be used in a comprehension
it's a little more nuanced than that
!e
[f for f in (d:="hello")]
@rapid sparrow :x: Your eval job has completed with return code 1.
001 | File "<string>", line 1
002 | SyntaxError: assignment expression cannot be used in a comprehension iterable expression
and my question is, why the hell not
and can we fix it
seems arbitrary
like a "let's explicitly disallow this"?
that error is in symtable.c, does that matter?
static int symtable_handle_namedexpr(struct symtable *st, expr_ty e) {
if (st->st_cur->ste_comp_iter_expr > 0) {
PyErr_Format(PyExc_SyntaxError, "assignment expression cannot be used in a "
"comprehension iterable expression");
PyErr_RangedSyntaxLocationObject(st->st_filename, e->lineno,
e->col_offset + 1, e->end_lineno,
e->end_col_offset + 1);
return 0;
}
if (st->st_cur->ste_comprehension) {
if (!symtable_extend_namedexpr_scope(st, e->v.NamedExpr.target))
return 0;
}
if (!symtable_visit_expr((st), (e->v.NamedExpr.value)))
return --((st))->recursion_depth, (0);
if (!symtable_visit_expr((st), (e->v.NamedExpr.target)))
return --((st))->recursion_depth, (0);
return 1;
}```
ok, gotcha
ste_comp_iter_expr
int ste_comp_iter_expr; /* non-zero if visiting a comprehension range expression */
never heard of such term
Ahhhh
/* bpo-37757: For now, disallow *all* assignment expressions in the
* outermost iterator expression of a comprehension, even those inside
* a nested comprehension or a lambda expression.
*/
if (prev) {
ste->ste_comp_iter_expr = prev->ste_comp_iter_expr;
}```
how un-fun
>>> [f for f in (d:="hello")]
['h', 'e', 'l', 'l', 'o']
tada
it didn't even crash, what could be the harm
in case anyone's curious to replicate
ok smart folks
due to a limitation in CPython's symbol table analysis process, the reference implementation raises SyntaxError for all uses of named expressions inside comprehension iterable expressions, rather than only raising them when the named expression target conflicts with one of the iteration variables in the comprehension
how can I check if this is actually a legitimate "'limitation"?
oh lmao, and?
did the world end
cool
in the para above, that's from the PEP, I think what they're doing is justifying the SyntaxError due to a "cpython limitation"
to me it seems more accurate to call it "cpython self-fulfilling prophesy", but anyway
exactly, theres none that I've observed
if there had been I would expect crashes.. I shouldprobably at least run their test suite though
before adding a bug
they can or can't?
aye
i agree, but there are older ones...
btw
that paragraph
has no citation
so glad folks who started threads like "A PEP-572 Call for prudence" didn't sway the implementors
did yours build @sick hound
can you run
[f for f in (d:="hello")]
so the complaint is that i'm assigning the thing i'm gonna loop through
i dont get the big deal
oh noice 🥳 🥳 🥳
how to do something even more hairy
and liable to violate the very tenet of language designers
your edit is way bigger
how about this, can you run
9: list
?
i get SyntaxError: illegal target for annotation
or
l = (yield f for f in (d:="hello"))
print(*l)``` ?
i cant yet
i know loll
but why not..
In symtable_handle_comprehension
i changed both
hoping number will annotate {somehow}
static int
symtable_raise_if_annotation_block(struct symtable *st, const char *name, expr_ty e)
{
if (1 || st->st_cur->ste_type != AnnotationBlock) {
return 1;
}
...
i dont know if i should disable that or permanentky short circuit it, haha
ok here we go
strike 1
