#esoteric-python
1 messages · Page 122 of 1
A PyObject is actually a pointer to a python object
!e from ctypes import py_object
print(py_object.from_buffer_copy(id(1).to_bytes(8, 'little')))
@rugged sparrow :white_check_mark: Your eval job has completed with return code 0.
py_object(1)
.
Why is it segfaulting there?
!e ```py
from ctypes import*
hack=lambda victim:py_object.from_address(id(victim))
victims = [range(32) ]
a = [hack(i)for i in victims]
print("before")
gulag = [yay.value for yay in a]
print('after')
new = [{*gulag}]
for i in a:
print(i)
i.value = new[i.value]
print (victims)
@floral meteor :x: Your eval job has completed with return code 139 (SIGSEGV).
before
If I don't reference victims at the end the object will be NULL.
But it segfaults reading the integers
I get 0xc0000005 not 139
Why doth thee fault thine segments?
!e ```py
class dunder(metaclass=lambda*a:type(*a)()):format=lambda s,spec:f'{spec}'
def dir(target=import(f'{dunder:main}')):
v=[*target.class.dict]
if hasattr(target,f'{dunder:dict}'):v+=[*target.dict]
return v
print(dir())
print(dir({}))
@floral meteor :white_check_mark: Your eval job has completed with return code 0.
001 | ['__repr__', '__getattribute__', '__setattr__', '__delattr__', '__init__', '__new__', '__dir__', '__dict__', '__doc__', '__name__', '__doc__', '__package__', '__loader__', '__spec__', '__annotations__', '__builtins__', 'dunder', 'dir']
002 | ['__repr__', '__hash__', '__getattribute__', '__lt__', '__le__', '__eq__', '__ne__', '__gt__', '__ge__', '__iter__', '__init__', '__or__', '__ror__', '__ior__', '__len__', '__getitem__', '__setitem__', '__delitem__', '__contains__', '__new__', '__sizeof__', 'get', 'setdefault', 'pop', 'popitem', 'keys', 'items', 'values', 'update', 'fromkeys', 'clear', 'copy', '__reversed__', '__class_getitem__', '__doc__']
reinvented dir
!e ```py
class Yeet(Exception):
def init(self, metadata):
self._metadata = metadata
super().init()
@property
def metadata(self):return self._metadata
def a():
def b(W):
def c():
def d():
def f():
raise Yeet(W)
f()
d()
c()
print ("This line won't run");
try:b("Hello World!")
except Yeet as yeet:print (yeet.metadata)
a()
@floral meteor :white_check_mark: Your eval job has completed with return code 0.
Hello World!
Bruhh
@crimson pumice here's a simple demonstration of the error-slinging I was talking about
Oh yeah that's really nice
!e @crimson pumice and here's the whileTrue-Switch event loop I was talking about```py
from random import randint as r
def event_loop():
while True:
switch = r(0,10)
if switch==0:
return print("done!")
elif switch==1:
print(1)
elif switch==2:
print(switch*2, switch**2)
elif switch==3:
print("third time lucky!")
elif switch==4:
print("Hello World!")
else:
print(NotImplemented)
event_loop()
@floral meteor :white_check_mark: Your eval job has completed with return code 0.
001 | Hello World!
002 | 4 4
003 | 4 4
004 | 1
005 | third time lucky!
006 | NotImplemented
007 | NotImplemented
008 | third time lucky!
009 | NotImplemented
010 | 1
011 | 1
... (truncated - too many lines)
Full output: https://paste.pythondiscord.com/lehuxuqime.txt?noredirect
try ctypes.cast(id(...), ctypes.py_object)
what does that do?
basically iirc from_address is intended to be used with CData objects, e.g. another py_object instance's id/addressof. cast does extra necessary housekeeping.
think these are the relevant parts in the source
from_address:
https://github.com/python/cpython/blob/main/Modules/_ctypes/_ctypes.c#L577
https://github.com/python/cpython/blob/80f33f266b4ad5925a3e58ea3a54ae139a6b6f0e/Modules/_ctypes/_ctypes.c#L3024
Can't you just directly create a new py_object with the thing instead of casting it?
as in py_object(...) (in the context of this)
oh cool, didnt know it took an argument. that saves some time
Wow - I've tried to do this in the past
I got number shuffling working, but couldn't do all of them
!e
import ctypes
ctypes.c_int8.from_address(id(4)+8).value=5
print(2+2)
@golden finch :warning: Your eval job has completed with return code 139 (SIGSEGV).
[No output]
oop
spicy
seems to segfault after the second line
I'll brute-force offsets until it works
0-8: No segfault
8-16: Segfault
16-?: Something
ctrl-c doesn't work
@tribal moon :x: Your eval job has completed with return code 139 (SIGSEGV).
4
well I got ... something
just not the right something
I did a stupid
!e
__import__('ctypes').c_int8.from_address(id(4)+16).value=id(5)
@golden finch :warning: Your eval job has completed with return code 0.
[No output]
[it hangs]
on closer inspection the value is 1
!e
import ctypes
def deref(addr,typ):
return ctypes.cast(addr,ctypes.POINTER(typ))
deref(id(4),ctypes.c_int)[6]=5
print(2+2)
@golden finch :white_check_mark: Your eval job has completed with return code 0.
5
@floral meteor got it working- idk how to shuffle though
ctypes is awesome
ctypes is just the best
!e
import ctypes
deref=lambda a,b:ctypes.cast(a,ctypes.POINTER(b))
deref(id(4),ctypes.c_int)[6]=5
print(2+2)
@golden finch :white_check_mark: Your eval job has completed with return code 0.
5
don't forget about the int representation
?
well, I assumed the initial attempt to mutate an integer was through accessing its digits
@golden finch it's faster to get the offsets by looking at cpython source code
But for reference, all python objects start with <0-8> ref count, <9-16> pointer to object type
Ref count is an 64 bit integer, and object type is a 64 bit pointer
Include/longintrepr.h lines 85 to 88
struct _longobject {
PyObject_VAR_HEAD
digit ob_digit[1];
};```
this channel is weird.
nice
!e ```py
metamaker = (lambda meta, cls: ( lambda: [ (dict_.pop(es, None) for es in dict_.get("slots", tuple())) if [None for [globals()["dict_"]] in [[dict(cls.dict)]]][0] is None else None, [None for [globals()["dict_"]["metaclass"]] in [[meta]]], [None for [globals()["dict"]["wrapped"]] in [[cls]]], meta(str(cls.name), tuple(cls.bases), dict), ][-1]))
printf = lambda format_string, *args: print(format_string,end="") if not args else print(format_string % args,end="")
main = metamaker(type("b", (type,), {"call": lambda self, : {None}}), type("", (object,), {}))()
int = metamaker(type("a", (type,), {"sub": lambda self2, : {None} if isinstance(, set) else [None for [globals()[.split("=")[0]]] in [[import('builtins').int(_.split("=")[1])]]][0]}), type("a", (object,), {}))()
void = None
return_0 = None
int-main(void)-{
printf("Hello, World!\n"),
int-"num1=2",
int-"num2=3",
printf("%d + %d = %d\n", num1, num2, num1+num2),
return_0
}```
@harsh spear :white_check_mark: Your eval job has completed with return code 0.
001 | Hello, World!
002 | 2 + 3 = 5
cool, now make that 2+2=5
everyone importing sqrt from math smh.
x**.5
is better than
sqrt(x)
and if you want to floor something, instead of importing floor from math...
x//1
all you need from math is constants like e and π
change my mind
haha wow somehow I never thought of that
Combinatorics and trigonometry. Greatest common denominator.
Logarithms.
Anything to do with manipulating floats precisely.
that's what algorithms are for ;)
that's what integers are for ;)
you just need... two integers
That's unconvincing. I could reimplement timezones using algorithms, but I have esoteric Python to do with my time instead.
So I discovered a really weird thing: when using ipython (which powers for example jupyter notebook) the is operator behaves really weirdly with strings that are longer than 1 character and contain non-alphanumeric characters.
This code prints false when run in ipython:
a = '.a'
b = '.a'
print(f'a is b: {a is b}')```This code prints true:
```py
a = 'abc123'
b = 'abc123'
print(f'a is b: {a is b}')```Any string with only alphanumeric characters seems to print true.
```py
a = '.'
b = '.'
print(f'a is b: {a is b}')```But this also returns true, in fact all strings of length 1 print true. But if the string is longer than 1 character it prints false.
Is this intended or a bug? What's causing it?
This is intended.
You'll likely find that very long strings with only alphanumeric characters also compare unequal with is.
Python makes no guarantees about which strings will be interned at the same address.
It seems to also depend on whether the string is made from operations on literals, such as adding constant strings together, or if it has intermediate steps.
In [4]: foo= "a"
In [5]: (foo + foo) is (foo + foo)
Out[5]: False
In [6]: ("a" + "a") is ("a" + "a")
<>:1: SyntaxWarning: "is" with a literal. Did you mean "=="?
<ipython-input-6-7cfee5715f00>:1: SyntaxWarning: "is" with a literal. Did you mean "=="?
("a" + "a") is ("a" + "a")
Out[6]: True
hmm, trying longer and longer alphanumeric strings, but they all print true.
so can this happen with regular python too if the string is long enough?
IPython is based on CPython, and only changes some of the user-facing things. I can't imagine regular Python would behave much differently.
It does behave differently, the examples I gave don't work in regular python, tho the example you gave works
PyCharm:
Google colab (which is based on jupyter):
!e print('yeet' is "yeet")
@floral meteor :white_check_mark: Your eval job has completed with return code 0.
001 | <string>:1: SyntaxWarning: "is" with a literal. Did you mean "=="?
002 | True
!e print(" 69"is" 69")
@floral meteor :white_check_mark: Your eval job has completed with return code 0.
001 | <string>:1: SyntaxWarning: "is" with a literal. Did you mean "=="?
002 | True
!e ```py
_ = chr(ord('6')) is '6'
print(_)
@dawn kayak :white_check_mark: Your eval job has completed with return code 0.
001 | <string>:1: SyntaxWarning: "is" with a literal. Did you mean "=="?
002 | True
!e
_ = ''
__ = []
for i in range(1 << 8):
_ += chr(i) + '!'
__.append(chr(i))
print(_[:-1] == '!'.join(__))
print(_[:-1] is '!'.join(__))
@slim yarrow :white_check_mark: Your eval job has completed with return code 0.
001 | True
002 | False
there we go
as you can see, is is not guaranteed to return True on equal strings
as for only alphanumeric characters:
so googling shows that this is string interning. by storing only one copy of equal strings python saves space and computing power, but I still don't understand why ipython does it differently form regular python.
!e
_ = ''
__ = []
for i in 'abcdefghijklmnopqrstuvwxy':
_ += i + '0'
__.append(i)
print(_[:-1] == '0'.join(__))
print(_[:-1] is '0'.join(__))
@slim yarrow :white_check_mark: Your eval job has completed with return code 0.
001 | True
002 | False
probably because ipython recieves input line-by-line, whereas python can just read from a file. getting all of the input at once allows for python to optimize this.
don't quote me i don't know what i'm talking about
that makes sense
as a test, you can try this:
a = '..'
b = '..'
print(a is b)
a, b = '..', '..'
print(a is b)```
the first is false in ipy, the second is true
as for the case with 1 character, probably because it's (maybe?) optimized to a c char
huh, even ipython isn't consistent across the board. In google colab both are false, in jupyter it's as you say
that's interesting
well I gotta go to sleep, but this was very interesting. I guess this is just another reason not to use is for comparison, not that I was using it before :)
if i had to guess, maybe it's because colab uses some different version of python (cuz it's containerized, so maybe a version compiled for alpine?)
since alpine uses musl rather than glibc maybe there's a difference there
but i really have no idea what i'm talking about
lol I have no idea what any of that means, but I will google this tomorrow, sounds interesting
just spun up a docker container with alpine; alpine isn't the reason for this difference
but it is because of a difference in python
colab uses cpython 3.7.11
after which (so 3.8+), this behavior changes
you can calculate pi and e with algorithms too
theoretically
in fact, theoretically, you can calculate it in brainfuck, so you just need too write a brainfuck interpeter
$ docker run --rm -i -t python:3.7-alpine python
Python 3.7.11 (default, Jun 29 2021, 21:06:34)
[GCC 10.3.1 20210424] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> a = '..'
>>> b = '..'
>>> a is b
False
>>> a, b = '..', '..'
>>> a is b
False
>>> exit()
$ docker run --rm -i -t python:3.8.0-alpine python
Python 3.8.0 (default, Nov 15 2019, 02:22:06)
[GCC 8.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> a = '..'
>>> b = '..'
>>> a is b
False
>>> a, b = '..', '..'
>>> a is b
True
>>> exit()
``` @narrow shard
```py
#code
```
would help the eyes very much
except it's not entirely python
I would rather not be investing in SpecSavers
I'm gonna start with the basic tail end recursion structure for mathematical generation```py
def __e(n=100):
v = ...
if n:
return v + __e(n-1)
else:
return v;
def __π(n=100):
v = ...
if n:
return v + __π(n-1)
else:
return v;
calculimificate = {
'e':__e,
'pi':__π
}
print(*(calculimificateafor a in calculimificate),sep='\n')
and then i'm gonna thonk for a solid few hours
I used to know how to do e
lim(n->inf){(1 + 1/n)^n}
nope i think i got the wrong formula
imma try sum to inf (1/n!)
it's the limit of that equation as n approaches infinity ya
!e ```py
def f(n):
return n*f(n-1)if n else 1
def _make_e(n=100):
if n:return f(n:=n-1)**-1 + _make_e(n);
else:return 0;
e = _make_e(100)
print(e)
@floral meteor :white_check_mark: Your eval job has completed with return code 0.
2.7182818284590455
there's e
!e print((10001/10000)**10000)
@simple crystal :white_check_mark: Your eval job has completed with return code 0.
2.7181459268249255
mines accurater
@simple crystal :white_check_mark: Your eval job has completed with return code 0.
2.7182682371922975
I'm so close to infinity though
@floral meteor :white_check_mark: Your eval job has completed with return code 0.
2.7182820532347876
compare it to 2.7182818284590455
and 'actual' is 2.7182818284590452
!e ```py
_make_e = lambda acc:((10acc+1)/(10acc))(10acc)
print(_make_e(12))
@floral meteor :white_check_mark: Your eval job has completed with return code 0.
2.7185234960372378
you try to get more accurate it get's less accurate
hmmm 10**29 gives a pretty good one but 10**30 is just 1.0 for some reason
with Decimal
!e ```py
def f(n):
return n*f(n-1)if n else 1
def _make_e(n=100):
if n:return f(n:=n-1)**-1 + _make_e(n);
else:return 0;
e = _make_e(150)
print(e)
with floats it gives up at 15 zeroes
@floral meteor :white_check_mark: Your eval job has completed with return code 0.
2.7182818284590455
!e
from decimal import Decimal
for x in range(27,30):
n=Decimal(10**x)
print(x, ((n+1)/n)**n)
there must be a floating point error somewhere
@simple crystal :white_check_mark: Your eval job has completed with return code 0.
001 | 27 2.718281828459045235360287470
002 | 28 1
003 | 29 1
ooh that one's good
need Decimal tho
gl
nah pycharm frozen im ggonna do it in vs code
python supports arbitrary integers, so i just need two integers
one for the data, one for the position of the decimal point
ah true
def __lt__(self, other):
a, b, c, d = self.n, self.offset, other.n, other.offset;
while b < d:
b += 1;
a *= 10;
while b > d:
d += 1;
c *= 10;
return a > c;
would that work?
where offset is like the log base 10 floored
and n is just the number without the dot
def __str__(self):
return ''.join([*str(self.n)].insert(self.offset,'.'))
that's how n and offset are defined
this channel is weird.
this channel is beautiful
!e
print((1097-(55**5+311**3-11**3)/68**5)**(1/7))
from math import e
print(math.e)```
@slim yarrow :x: Your eval job has completed with return code 1.
001 | 2.7182818284590455
002 | Traceback (most recent call last):
003 | File "<string>", line 3, in <module>
004 | NameError: name 'math' is not defined
i'm an idiot
!e
print((1097-(55**5+311**3-11**3)/68**5)**(1/7))
from math import e
print(e)```
@slim yarrow :white_check_mark: Your eval job has completed with return code 0.
001 | 2.7182818284590455
002 | 2.718281828459045
i moved the offset to represent from the right
my equils algerithim ```py
def eq(self, other):
return self>=other>=self
def __ge__(self, other):return not self<other;
def __le__(self, other):return not self>other;
big brain go brrr
Wanna hear a fun idea? Try to make your own COMPLETELY different syntax "mini-language" within Python by messing with the opcodes and objects (sys, dis, and ctypes) and see how far you can go
I really really want to see someone do this
I would start by hacking globals
def __eq__(self, other):
return ~-self < other and -~self > other
and i did it already, in a way
open a github repository or something and do it
I'd love to see the progress
do you know how to rearrange/edit a frame's opcodes?
because it looks way better than using ctypes and stuff
well it is using ctypes but whatever
I tried to make one a while back and I just used dunders and stuff
it kinda looked like a new language
but it still looked like Python
why not just make a new language
@tribal moon you mean like this?
oh you want something like basicfuck?
Hold on a second
Something like this: #esoteric-python message
I used some dunder magic to make it #esoteric-python message
I wanna see someone do something like this, except without dunder stuff
just with changing the frames
and a lot of ctypes stuff
annotations can also be used
to reverse the polarity of type hints
and/or enforce them
sure that could work but that's not the point
see the lines that make arrays
something = Array<int>()```
I had NO idea how to do something like that
that's mostly brute force stack modification
the only way was to change the bytecode for the frame and stuff, not sure how it works
chilaxan made a really good example of code that does this
<int> would be rejected as invalid syntax, but im sure with black magic you can make it legal
that's the point
!e ```py
def legal():
Array<int>()
@dawn kayak :warning: Your eval job has completed with return code 0.
[No output]
maybe i'm stupid but why would that syntax be invalid
damn its legal
here: #esoteric-python message read this
looks like chained comparisons
maybe some ast hacking decorator ?
this is how it was made
really good code
appreciate it
I'm still trying to replace UNARY_POSITIVE UNARY_POSITIVE with LOAD_CONSTANT(1) BINARY_ADD DUP_TOP ASSIGN_VALUE
dis.get_instructions that's what i need
anyways i'm already working on arbitrary precision floats
I will name my invention...
"doubles"
;)
tbh I don't really like the changes made after 3.7
walrus, patma, that, list[int] n stuff
importing typing is dinky
type hints that don't do anything are dinky
I like how you can just name your variables whatever you want
round=lambda floater:(floater+0.5)//1
!e ```py
from future import annotations
import builtins as new
@lambda c:c()
class annotations:
def setitem(self, a, b):
globals()[b] = new.dict [a] (globals()[a])
class System:
class out:
println,print=print,lambda s:print(end=s)
str: a = 6;
str: b = 9;
int: c = new.int (a + b);
System.out.println(c);
@floral meteor :white_check_mark: Your eval job has completed with return code 0.
69
what in the world is this channel
this is the torture chamber for python
ah i see
!e ```py
from signal import*
from ctypes import*
signal(11, print)
string_at(0)
XD
@floral meteor :warning: Your eval job timed out or ran out of memory.
[No output]
oh it didn't print
o_o
I thought System.out.println was java....
it is
it is in literally every other channel than this one
this chat is my kind of chat
!e ```py
print(0x00for x in input("Hello World!"))
@floral meteor :white_check_mark: Your eval job has completed with return code 0.
15
I ate a chocolate i found in my car now my teeth hurt
did you hear about 3.10s int.jor() and int.jand() additions?
!e java print(100.jor(), 100.jand())
@tribal moon :white_check_mark: Your eval job has completed with return code 0.
100j ()
lol
@floral meteor :white_check_mark: Your eval job has completed with return code 0.
5
!e ```py
print(import('sys').version)
@floral meteor :white_check_mark: Your eval job has completed with return code 0.
001 | 3.9.6 (default, Jun 29 2021, 19:27:32)
002 | [GCC 8.3.0]
it's still on 3.9
just declare Array as a custom class
class Array:
def __init__(self,contents=[]):
self.contents=contents
def __le__(self,other):
return Array(self.contents)
def __ge__(self,other):
globals().update(...)
something like this
!e ```py
print((100).jor.doc)
Hahaha
!e ```py
print(100.jor())
@floral meteor :white_check_mark: Your eval job has completed with return code 0.
100j
!e ```py
print(100.jor(), (0xc0000000for x in 100% yo_mamma is fat),)
@floral meteor :white_check_mark: Your eval job has completed with return code 0.
100j 51539607567
my pycharm is having a heart attack right now my god
What's the syntax highlighting here?
what i have so far on the double precision float ```py
class double:
def init(self, a):
try:self.n,self.o=a;
except Exception:
f,=a;o=0;
while f>f//1:o+=1;f=10;
self.n,self.o=int(f),o;
def float(self):
return self.n / 10**self.o
def index(self):return self.n
def str(self):
k=len(str(n:=self.n));
return''.join((a:=[str(n)]).insert(k-self.o,'.')or a);
def repr(self):return str(self);
def eq(self, other):return self>=other>=self;
def reduce(self):
return self.n, self.o;
def lt(self, other):
try:a, b, c, d = self.n, self.o, other.n, other.o;
except Exception:return type(other)(self)<other;
else:
while b<d:b+=1;a=10;
while b>d:d+=1;c*=10;
return a<c;
def gt(self, other):
try:a, b, c, d = self.n, self.o, other.n, other.o;
except Exception:return type(other)(self)>other;
else:
while b<d:b+=1;a*=10;
while b>d:d+=1;c*=10;
return a>c;
def ge(self, other):return not self<other;
def le(self, other):return not self>other;
includes the intuitive i-dont-give-a-fuck typing system
thats really cool
i'm gonna give it a few maximum precision mathematical operations now
and they're all gonna work with integers only
pep 263 is another way of implementing syntax hackery
!pep 263
!e
import codecs
import typing
import io
def encode(text: str) -> typing.Tuple[bytes, int]:
raise NotImplementedError
def decode(binary: bytes) -> typing.Tuple[str, int]:
result = io.StringIO()
buffer = io.BytesIO(binary)
for line in buffer:
if line.startswith(b'#'):
continue
if line.startswith(b'namespace'):
name = line.split()[1][:-1].decode()
result.write(f'class {name}:\n')
else:
result.write(line.replace(b'::', b'.').decode())
return result.getvalue(), result.tell()
codecs.register(
lambda name: codecs.CodecInfo(encode=encode, decode=decode, name='namespaces')
)
code = b"""
# coding=namespaces
namespace example:
a = "example value"
print(example::a)
"""
exec(code)
@woven bridge :white_check_mark: Your eval job has completed with return code 0.
example value
ast wont work with weird stuff that causes syntax errors
that's very interesting
I'd still love to see people combine that, frames and bytecode stuff, and ctypes to make something really cool
once you register a codec then it can be used in any modules that get imported afterwards with # coding=[name] as the first line.
but by then it would be an interpreter of another language
had to condense it all into one snippet for the bot.
otherwise it'd be something like this:
file_1.py
import namespaces_codec
namespaces_codec.register() # does the codecs.register(...) part
import file_2
file_2.test()
file_2.py
# coding=namespaces
namespace example:
a = "example value"
def test():
print(example::a)
try to recreate C or something like that
too bad you can't do something like like !e https://paste.pythondiscord.com/paste/something.py
so you'd need to like compress it or something
one thing im a little confused by is why the decoder needs to return both a string and its length
maybe it can return any indexable, even those without a __len__
havent investigated it
!e here's the official class override ```py
class hack:
def new(cls, a, *b):
from ctypes import py_object as p
return super().new(cls)if b else p.from_address(id(a)+8)
@property
def value(self):
"""Kept here just to keep pycharm happy."""
from ctypes import py_object as p
return p.from_address(id(self)+8).value
@value.setter
def value(self, v):
from ctypes import py_object as p
p.from_address(id(self)+8).value = v
def init(self, *pairs):
v = self.victims = []
for i,j in zip(pairs[::2],pairs[1::2]):
if i not in[k for k,_ in v]:v.append((i,hack(i).value))
hack(i).value = j
def enter(self):return self
def exit(self, a, b, c):
self.del()
return a is b is c is None
def del(self):
for victim, prisoner in self.victims:
hack(victim).value = prisoner
with hack(True, int, False, int):
print (False, not 0, not 1)
print (False, not 0, not 1)
with hack(..., type('std::cout',(),{
'lshift':lambda s,o:print(end=o)or s,
'endl':'\n'
})):
... << "Hello World!" << ....endl;
@floral meteor :white_check_mark: Your eval job has completed with return code 0.
001 | 0 1 0
002 | False True False
003 | Hello World!
!e ```py
class hack:
def new(cls, a, *b):
from ctypes import py_object as p
return super().new(cls)if b else p.from_address(id(a)+8)
@property
def value(self):
"""Kept here just to keep pycharm happy."""
from ctypes import py_object as p
return p.from_address(id(self)+8).value
@value.setter
def value(self, v):
from ctypes import py_object as p
p.from_address(id(self)+8).value = v
def init(self, *pairs):
v = self.victims = []
for i,j in zip(pairs[::2],pairs[1::2]):
if i not in[k for k,_ in v]:v.append((i,hack(i).value))
hack(i).value = j
def enter(self):return self
def exit(self, a, b, c):
self.del()
return a is b is c is None
def del(self):
for victim, prisoner in self.victims:
hack(victim).value = prisoner
with hack(0,float,1,float):
print(0, 1)
print(0, 1)
@floral meteor :x: Your eval job has completed with return code 1.
001 | Traceback (most recent call last):
002 | File "<string>", line 27, in <module>
003 | File "<string>", line 17, in __init__
004 | File "<string>", line 3, in __new__
005 | TypeError: __int__ returned non-int (type float)
006 | Exception ignored in: <function hack.__del__ at 0x7fea387823a0>
007 | Traceback (most recent call last):
008 | File "<string>", line 25, in __del__
009 | File "<string>", line 3, in __new__
010 | TypeError: __int__ returned non-int (type float)
I think i broke something
XDDDDD
!e ```py
import sys
poopy = sys.meta_path
class hack:
def new(cls, a, *b):
from ctypes import py_object as p
return super().new(cls)if b else p.from_address(id(a)+8)
@property
def value(self):
"""Kept here just to keep pycharm happy."""
return import('ctypes').py_object.from_address(id(self)+8).value
@value.setter
def value(self, v):
import('ctypes').py_object.from_address(id(self)+8).value = v
def init(self, *pairs):
v = self.victims = []
for i,j in zip(pairs[::2],pairs[1::2]):
if i not in[k for k,_ in v]:v.append((i,hack(i).value))
hack(i).value = j
def enter(self):return self
def exit(self, a, b, c):
self.del()
return a is b is c is None
def call(self, other):
return self.class(other)if other is not self else self
def del(self):
sys.meta_path = poopy
for victim, prisoner in self.victims:
import('ctypes').py_object.from_address(id(victim)+8).value = prisoner
with hack(hack, hack) as h:
print(h, hack, hack.class, hack.class is hack.class.class is hack)
@floral meteor :white_check_mark: Your eval job has completed with return code 0.
001 | <__main__.hack object at 0x7fe40f2446d0> <__main__.hack object at 0x5599532f2b50> <__main__.hack object at 0x5599532f2b50> True
002 | Exception ignored in: <function hack.__del__ at 0x7fe40f2464c0>
003 | Traceback (most recent call last):
004 | File "<string>", line 28, in __del__
005 | File "<frozen importlib._bootstrap>", line 1007, in _find_and_load
006 | File "<frozen importlib._bootstrap>", line 982, in _find_and_load_unlocked
007 | File "<frozen importlib._bootstrap>", line 925, in _find_spec
008 | File "<frozen importlib._bootstrap_external>", line 1414, in find_spec
009 | File "<frozen importlib._bootstrap_external>", line 1380, in _get_spec
010 | TypeError: 'NoneType' object is not iterable
why can't i finish deleting this bad boy?
smh
!e ```py
import sys
poopy = sys.meta_path
class hack:
def new(cls, a, *b):
from ctypes import py_object as p
return super().new(cls)if b else p.from_address(id(a)+8)
@property
def value(self):
"""Kept here just to keep pycharm happy."""
return import('ctypes').py_object.from_address(id(self)+8).value
@value.setter
def value(self, v):
import('ctypes').py_object.from_address(id(self)+8).value = v
def init(self, *pairs):
v = self.victims = []
for i,j in zip(pairs[::2],pairs[1::2]):
if i not in[k for k,_ in v]:v.append((i,hack(i).value))
hack(i).value = j
def enter(self):return self
def exit(self, a, b, c):
self.del()
return a is b is c is None
def call(self, other):
return self.new(self, other)
def del(self):
sys.meta_path = poopy
for victim, prisoner in self.victims:
import('ctypes').py_object.from_address(id(victim)+8).value = prisoner
with hack(hack, hack):
print(hack is hack.class)
print(dir(hack))
@floral meteor :x: Your eval job has completed with return code 139 (SIGSEGV).
001 | True
002 | []
@floral meteor :white_check_mark: Your eval job has completed with return code 0.
[] [(<__main__.hack object at 0x56144d8bd160>, <class 'type'>)]
if youre really determined to you could replace the parser machine code 😛
but thatd be a pain in the back and a half
!e ```py
import sys
poopy = sys.meta_path
class hack:
victims = []
def new(cls, a, *b):
from ctypes import py_object as p
return super().new(cls)if b else p.from_address(id(a)+8)
@property
def value(self):
"""Kept here just to keep pycharm happy."""
return import('ctypes').py_object.from_address(id(self)+8).value
@value.setter
def value(self, v):
import('ctypes').py_object.from_address(id(self)+8).value = v
def init(self, *pairs):
v = self.victims = []
for i,j in zip(pairs[::2],pairs[1::2]):
if i not in[k for k,_ in v]:v.append((i,hack(i).value))
hack(i).value = j
def enter(self):return self
def exit(self, a, b, c):
self.del()
return a is b is c is None
def call(self, a, *b):
return self.new(a, *b)
def del(self):
sys.meta_path = poopy
for victim, prisoner in self.victims:
import('ctypes').py_object.from_address(id(victim)+8).value = prisoner
with hack(hack,hack) as h:
print(h(h,h))
del h
I did find this super cool article on something like that though: https://eli.thegreenplace.net/2010/06/30/python-internals-adding-a-new-statement-to-python/
should give it a read
@floral meteor :white_check_mark: Your eval job has completed with return code 0.
py_object(<__main__.hack object at 0x560287406230>)
hack has hacked itself to become it's own class, but that's really messing with python
Do you put all of this in a github repositroy or something
i have a feeling it might actually not be that hard with the correct tool
can't do it with ctypes
you can use os api calls to overwrite any process memory using ctypes
I know
so then why say you cant do it with ctypes
Prove me wrong
What's the easiest way to bluescreen in Python?
challenge possibly accepted 😛
good luck!
bonus points if no ctypes
Also, I was reading a SO answer recently
eval((lambda:0).__code__.replace(co_consts=()))
it obviously segfaults
but the answer said that it had the potential for arbitrary code execution "if you are smart about it"
how?
Link?
!e ```py
import sys
poopy = sys.meta_path
class hack:
victims = []
def repr(self):
return'<hacked '+str([yay[0]for yay in self.victims])+'>'if self.victims else'<class hack>'
def new(cls, a, *b):
from ctypes import py_object as p
if not b:return p.from_address(id(a)+8)
else:
return super().new(cls)
@property
def value(self):
"""Kept here just to keep pycharm happy."""
return import('ctypes').py_object.from_address(id(self)+8).value
@value.setter
def value(self, v):
import('ctypes').py_object.from_address(id(self)+8).value = v
def init(self,*pairs):
v = self.victims = []
for i,j in zip(pairs[::2],pairs[1::2]):
if i not in[k for k,_ in v]:v.append((i,hack(i).value))
hack(i).value = j
def enter(self):return self
def exit(self, a, b, c):
self.del()
return a is b is c is None
def call(self, a, *b):
return self.new(self.class,a, *b)
def del(self):
sys.meta_path = poopy
for victim, prisoner in self.victims:
import('ctypes').py_object.from_address(id(victim)+8).value = prisoner
with hack(hack, hack) as h:
print(hack, h, hack.class)
hack(hack).value = h
hack.victims#SIGSEV
del h;
print('can',hack,'be unhacked?')#yes
Too vague to find out
@floral meteor :x: Your eval job has completed with return code 139 (SIGSEGV).
<class hack> <hacked [<class hack>]> <class hack>
not so
but se
@woven bridge any luck?
nah, reading the parser source and that article you linked, still thinking of an approach
also wanna look deeper into the stuff numba is doing with llvm, might be useful
cause a bsod?
might be possible to replace the parser instructions entirely with a precompiled replacement bundled with the script, so it would be python only for the end user, but creating the script itself would require another compiler
aside from that im thinking if there are smarter ways to go about it than just replacing the entire thing
messing with python at runtime to implement a true new operator
Can only use Python
it would simplistically boil down to something like this
instructions = b"very long byte string"
inject(instructions)
the instructions would be generated using something else
but the script itself would ultimately be only python
is that within the rules? 😅
You can do ANYTHING you want
as long as it's Python
even if you use C functions
it doesn't matter
as long as it can be ran within Python
got it
lol
i heard they're going to be removed in 3.11 :>
Is there a way to have a class’ “parent” be itself? I want to allow for things like this:
class a:
def __getitem__(self, idx):
return “test”
print(a[10])
In this case you could use __class_getitem__, for other dunders you would want to define a metaclass
e.g.
class a:
def __class_getitem__(cls, idx):
return "test"
print(a[10])
or with a metaclass
class Meta(type):
def __getitem__(self, idx):
return "test"
class a(metaclass=Meta):...
print(a[10])
what else __class__ dunders are there?
None others like __class_getitem__ iirc
oh only this?
!pep 560
was added as part of that
oh i see
btw, remembered this: https://github.com/dropbox/pyxl
cool use of pep 263
i just learnt you can use the gcc preprocessor on non-c files
so you can do gcc -E - < main.py and it will preprocess it
you know what this means right....
MACROS!!!
yup, CPP works on arbitrary files
exactly
eh, i thought #include works with only .h but seems it works with any file
why is this so fun
Wait what
yes
you can use macros in python
because gcc can preprocess ANY file
from subprocess import run
o = run('gcc -E - < file.py', shell=True, capture_output=True)
exec(o.stdout)
this is what i use to run the file
why use import when you can use #include
altho, are there any serious uses of this?
i suppose not
but this is hecka fun
me too
i just ripped it off of a stackoverflow answer
Just pipes the content of file.py into gcc, right?
The bare - in the gcc command says to read from stdin
as < file.py provides it into stdin
rather than as part of the command
is there a more convenient way to do this than capturing output and the exec'ing it
it literally copy pastes that file
this is the gcc preprocessor, it doesn't care about __all__
it just straight up copies the file onto the other file
so // This is include.py print("Hi!!!") and py (This is main.py) #include "include.py" print("Bye!") it becomes ```py
print("Hi!!!")
print("Bye")
look at this
it literally copy pasted stdio.h
thats why you get that weird error
that's the cpp for you
c doesnt really have an import system, you literally copy paste all the files 😂
using #include ofcourse
yup
luckily c++ has namespaces
looks like preprocessor leaves normal python comments alone
which makes these even better
unfortunately i can't replace : in python because its not a valid c token
aktually
it is valid
no more :
the macros are in include.py
and i include them
it doesnt actually need to be py
what ridiculous extension name should i give it 🤔
perfect
Looks like this sort of thing is deprecated in Python 3.10, actually.
It'll raise a warning.
It's listed here: https://docs.python.org/3.10/whatsnew/3.10.html#deprecated
Right at the top of the deprecations.
when will 3.10 be out of beta?
The release candidate was slated for three days ago, and is presumably out.
https://www.python.org/dev/peps/pep-0619/ Here's the agenda.
fuck ya
explicit do and ends in a forced tabs lang 
Why not…?
i dont know, but they give errors
Oh
How does the preprocessing work? Does it accept the -o parameter or does it replace the source file?
Yes but like does it produce an output file?
it sends it to stdout, which i capture and then execute
from subprocess import run
o = run('gcc -E - < file.py', shell=True, capture_output=True)
exec(o.stdout)
this is the main script
nice lol
!e py swapcase = lambda string: "".join(chr(asc+32) if (asc:=ord(char)) in range(65, 91) else chr(asc-32) if asc in range(97, 123) else char for char in string) print(swapcase("hEllO WorLd!?#")) quick implementation of str.swapcase without using any str-methods 'cause why not
@last locust :white_check_mark: Your eval job has completed with return code 0.
HeLLo wORlD!?#
thats smart
I guess the ability to toggle a bit for case was part of the design
swapcase = lambda s:"".join(chr(a+32)if(a:=ord(c))in range(65,91)else chr(a-32)if a in range(97, 123)else c for c in s)
```becoming```py
swapcase = lambda s:"".join(chr(a^32)if a|32 in range(97,123)else c for c in s)```?
Nvm that doesn't work @earnest wing
(a:=ord(c))
Ah yeah
Still not working
swapcase = lambda s:"".join(chr(a^32)if(a:=ord(c))|32 in range(97,123)else c for c in s)```
SyntaxError at the for
@earnest wing
what's ^ in py
bitwise xor
hrm i got it to 114 chars
swapcase=lambda s:"".join(map(lambda c:chr(c+32*[0,1,-1][2*(c in range(97,123))+(c in range(65,91))]),s.encode()))
My original was 117, Olivia's is 86 lol
!e ```py
x = "lambda s:"".join(map(lambda c:chr(c+32*[0,1,-1][2*(c in range(97,123))+(c in range(65,91))]),s.encode()))"
print(len(x))
@pure flame :white_check_mark: Your eval job has completed with return code 0.
103
hmm mine is shorter with the for instead of a map
You need ' around it not " because the lambda has "
swapcase=lambda s:"".join(chr(c+32*[0,1,-1][2*(c in range(97,123))+(c in range(65,91))])for c in s.encode())
ah
swapcase=lambda s:"".join(chr(a^32)if(a:=ord(c))|32 in range(97,123)else c for c in s)
Wait that's not right
There we go
So mine is slightly shorter
Or rather, Olivia's is
shaved 3 bytes off of that
swapcase=lambda s:"".join([c,chr((a:=ord(c))^32)][a|32 in range(97,123)]for c in s)
bonjour
a|32 in range(97,123)
96<a|32<123
that
and use ordered string comparison
swapcase=lambda s:"".join([c,chr((a:=ord(c))^32)][96<a|32<123]for c in s)
you missed a ] at the end
Nope
join([ should end with ] right?
It's [c,chr(...)^32][96<a....]
>>> swapcase=lambda s:"".join([c,chr((a:=ord(c))^32)][96<a|32<123]for c in s)
>>> print(swapcase("HeLLo WorLd042#!"))
hEllO wORlD042#!
73 👀
wait what happened i've gone for only 1 minute+
lel
lol
What do you mean?
Replacing with the appropriate ascii values?
just for fun because I like this pattern:
lambda s:"".join([c,chr(ord(c)^32)][all(eval("%r<c<%r,"*2%(*"@[`{",))]for c in s)
Just for fun because I like this pattern:
!ban 156021301654454272 We don't do fun here 👢
take notes kids:
- unpack into tuples using
(*blah,) %rformatter for strings*and%conveniently have the same precedence
is regex cheating
nvm i don't think it's possible
but i'm close to. so i should stop thinking of another solution
you can pass a lambda into re.sub
without using any str-methods
lmao i just realised that we're using"".joinwhich is a str method
Errr
Let's just ignore that

Can you unpack it or something instead?
we could have abused sum but for some reason its pretty explicit about not liking strings
you could also justlambda s:b"".join([c,chr(ord(c)^32)][all(eval("%r<c<%r,"*2%(*"@[`{",))]for c in s).decode() :)
How would that work?
"%c"*len(s)%tuple(your iterable here)
wait this is correct
lol
!e py swapcase = lambda s:"%c"*len(s)%tuple(chr(a+32)if(a:=ord(c))in range(65,91)else chr(a-32)if a in range(97, 123)else c for c in s) print(swapcase("HeLLo WorLd042#!"))
@last locust :white_check_mark: Your eval job has completed with return code 0.
hEllO wORlD042#!
how does this work
||arguably that also calls a str method, str.__mod__||
iterable of 1-length strings
god damn it
mul and mod
yeah
Ignoring dunders and stuff
just encode bytes :v)
i'll pretend i didn't see that
then you can't concat strings because its __iadd__ or __add__
actually every string you spawn makes a new string class which called str.__init__? idk
grrr
yeah lets just ignore that
implementation detail
i cheated
swapcase=__import__("builtins").str.swapcase
yes
just because you're accessing a string method indirectly doesn't mean you're not accessing it
assert builtins.str is str
!
swapcase=lambda s:s.swapcase
``` 
strictly speaking iterating over a string directly calls str.__iter__ :>
str.swapcase
i'm not good enough to understand a single word.
i think this returns a function you need to do ()? idk
ye
yeh swapcase(str)
did everyone giveup i see no new messages
is this too long
swapcase=lambda s:"%c"*len(s)%tuple([chr(ord(c)+(32 if ord(c)in range(65,91)else-32 if ord(c)in range(65,123)else-0))for c in s])
1 char close to #esoteric-python message
oops i used an extra space
seems like everyone gave up alr
nvm i can still remove two chars
swapcase=lambda s:"%c"*len(s)%tuple(chr(ord(c)+(32 if ord(c)in range(65,91)else-32 if ord(c)in range(65,123)else-0))for c in s)
nvm i lost
obligatory recursion
swapcase=lambda s:chr(c+32if(64<(c:=ord(s[0]))<92)else(c-32)if(96<c<124)else c)+swapcase(s[1:])if s else''
!e py swapcase=eval(bytes('慬扭慤猠∺⸢潪湩嬨Ᵽ档⡲愨㴺牯⡤⥣帩㈳崩㥛㰶籡㈳ㄼ㌲晝牯挠椠⥳','u16')[2:]) print(swapcase("HeLLo WorLd042#!"))
@slim yarrow :white_check_mark: Your eval job has completed with return code 0.
hEllO wORlD042#!
!e py print(len("eval(bytes('慬扭慤猠∺⸢潪湩嬨Ᵽ档⡲愨㴺牯⡤⥣帩㈳崩㥛㰶籡㈳ㄼ㌲晝牯挠椠⥳','u16')[2:])"))
@slim yarrow :white_check_mark: Your eval job has completed with return code 0.
57
how'd you do the let ... ?
aw well it was fun while it lasted lol
Weird
maybe 100.jand would work
and, like they mentioned, something like 0xfor something wouldn't
but it's the same thing though
idk
you can do an empty define ```c
#define let
is using the gdb python api ok?
What do you mean gdb python api?
gdb has a python api, and can compile and inject code into a process on the fly
so that could be one approach
I don't know, the goal is to make a payload that can work within Python itself
you can use gdb to debug
you can use anything it doesn't matter
ive found some really interesting python injectors but they all seem to rely on ctypes and gdb
theres even stuff that can inject a python interpreter into any process
cool af
im just genuinely curious to see how easy this could be
there are probably people in the server who are more knowledgeable on reverse engineering and stuff who can already do this
ive never really done anything like this before so still pretty much learning the basics
alright we'll see how you do
maybe if you have any questions with gdb I can try to help you
ik a way to inject and run assembly with only one import (for mmap)
What's the goal?
operators
operators in physics or operators in programming?
physics
you want to add a new operator at runtime?
just joking programming
I was thinking what the fuck
my background is physics so I was scared for a second there
well actually i was wondering, does windbg have on the fly compilation/injection too? i see theres a dll injection extension for it but i dont see any docs on being able to do the same kind of injection gdb can do
the goal is to basically inject into the python process at runtime to alter the parser at the lowest level
and implement a new operator
you could use a .pth file (gets ran before main file is parsed) to do your modifications
so what are you trying to do with operators?
trying to implement a new one on the machine code level at runtime, as if you wrote your own fork of cpython.
sounds interesting, can you elaborate on this?
so an entirely new operator - say a ` operator
yes
that's... terrifying
.pth files are ran at every python startup as they are meant to make a corresponding module work properly. they are intended to be low impact
so the idea with this would be to set up an import hook at startup?
what if... you make a python script called, say, compiler.py, that takes your script, runs it through compile(), and then messes with the bytecode, and then passes it to python?
yea
thats the easy way yes
so why can't you do that?
!pypi brm
already been done
so what are you trying to do then?
what the hell
what if you read file?
so you want something you can put at the start of your python script to allow for new operators?
^ wanna see how low level this can go realistically
ctypes x memory editing
yep, easier said than done though
the goal is to directly alter the cpython parser at runtime with an injection
i mean if we can figure out exactly what function always gets called for parsing we could just hook it and inject some new assembly
theres a whole bunch of shit that needs to be changed for it not to break the interpreter catastrophically
aside from a single entry point
one sec there was an article SomeDude posted a while back that detailed the process
ping me if you can find it
might be out of date by now though
also statement centric
looks like the general steps for implementing new syntax are still more or less the same, just with a new backend
true but that is if you are doing it at the C level
we kinda have to do it manually, prob with some fun assembly
assembly 
!e ```py
from ctypes import util
from ctypes import *
import atexit
base_size = sizeof(c_void_p)
libc = cdll.LoadLibrary(util.find_library('c'))
PAGE_SIZE = libc.getpagesize()
MEM_READ = 1
MEM_WRITE = 2
MEM_EXEC = 4
ENDIAN = 'little' if memoryview(b'\1\0').cast('h')[0]==1 else 'big'
libc.mprotect.argtypes = (c_void_p, c_size_t, c_int)
libc.mprotect.restype = c_int
def mprotect(addr, size, flags):
addr_align = addr & ~(PAGE_SIZE - 1)
mem_end = (addr + size) & ~(PAGE_SIZE - 1)
if (addr + size) > mem_end:
mem_end += PAGE_SIZE
memlen = mem_end - addr_align
libc.mprotect(addr_align, memlen, flags)
def addr(cfunc):
ptr = c_void_p.from_address(addressof(cfunc))
return ptr.value
def hook(cfunc, restype=c_int, argtypes=()):
cfunctype = PYFUNCTYPE(restype, argtypes)
cfunc.restype, cfunc.argtypes = restype, argtypes
o_ptr = addr(cfunc)
mprotect(o_ptr, 5, MEM_READ | MEM_WRITE | MEM_EXEC)
mem = (c_ubyte5).from_address(o_ptr)
default = mem[:]
def wrapper(func):
@cfunctype
def injected(*args, **kwargs):
try:
mem[:] = default
return func(*args, **kwargs)
finally:
mem[:] = jmp
n_ptr = addr(injected)
offset = n_ptr - o_ptr - 5
jmp = b'\xe9' + (offset & ((1 << 32) - 1)).to_bytes(4, ENDIAN)
mem[:] = jmp
@atexit.register
def unhook():
mem[:] = default
injected.unhook = unhook
return injected
return wrapper
@hook(pythonapi.PyDict_SetDefault, restype=py_object, argtypes=[py_object]*3)
def setdefault(self, key, value):
if key == 'MAGICVAL':
return self
return pythonapi.PyDict_SetDefault(self, key, value)
pythonapi.PyUnicode_InternFromString.restype = py_object
interned = pythonapi.PyUnicode_InternFromString(b'MAGICVAL')
setdefault.unhook()
print(interned)```
@rugged sparrow :white_check_mark: Your eval job has completed with return code 0.
{'__getattribute__': '__getattribute__', '__getattr__': '__getattr__', '__setattr__': '__setattr__', '__delattr__': '__delattr__', '__repr__': '__repr__', '__hash__': '__hash__', '__call__': '__call__', '__str__': '__str__', '__lt__': '__lt__', '__le__': '__le__', '__eq__': '__eq__', '__ne__': '__ne__', '__gt__': '__gt__', '__ge__': '__ge__', '__iter__': '__iter__', '__next__': '__next__', '__get__': '__get__', '__set__': '__set__', '__delete__': '__delete__', '__init__': '__init__', '__new__': '__new__', '__del__': '__del__', '__await__': '__await__', '__aiter__': '__aiter__', '__anext__': '__anext__', '__add__': '__add__', '__radd__': '__radd__', '__sub__': '__sub__', '__rsub__': '__rsub__', '__mul__': '__mul__', '__rmul__': '__rmul__', '__mod__': '__mod__', '__rmod__': '__rmod__', '__divmod__': '__divmod__', '__rdivmod__': '__rdivmod__', '__pow__': '__pow__', '__rpow__': '__rpow__', '__neg__': '__neg__', '__pos__': '__pos__', '__abs__': '__abs__', '__bool__': '__bool__', '__invert__
... (truncated - too long)
Full output: too long to upload
i've had to do stuff with assembly before when i implemented this C function hooker
it gets the address of the C function PyDict_SetDefault and sticks a relative jump instruction right at the start that jumps to my hooks C address, which unpatches PyDict_SetDefault, calls the python hook function, and then repatches PyDict_SetDefault
it does the unpatching and repatching to ensure the original C function works inside the python hook
oh i see
damn thats pretty neat
thanks for sharing @rugged sparrow
do you have a repo or something of other stuff like this?
oh also, whats the goal of the last 4-5 lines?
https://github.com/chilaxan/pysnippets is my random stuff repo
cheers!
oh i wrote asm_hook to get a reference to pythons interned strings dictionary
@woven bridge just pushed from my machine so it should have just got a bunch of random stuff
f_locals.py is one im particularly proud of, it makes the dictionary returned by locals() modify the local scope
awesome, gonna check it out
that's a feature python needs
thats why i wanted to implement it
Yes but it still leaves a space
Oh wait nvm figured it out
Welcome to the esoteric python channel where we break python
So a while ago there was a challenge on here to deconstruct a dictionary (make a, b, c = deconstruct({'a':1, 'c':3, 'd':4, 'b':2}) do the logical thing).
Recently I've been wondering about whether it would be possible to do a with statement:
d = {'a':1, 'c':3, 'd':4, 'b':2}
with deconstruct({'a':1, 'c':3, 'd':4, 'b':2} as c,b,a:
a+=1
b-=1
c*=2
print(d)
#should output:{'a':2, 'c':6, 'd':4, 'b':1} ```
I suspect that think I could modify my previous deconstruct() to handle that sort of call environment. (and return an object that was effectively a tuple with an enter / exit? But getting access to the locals environment in __exit__() might be tricky.
you can do that by hooking into the namespace
and collaborating that with the __iter__() of the deconstruct return
the only ways I know to hook that is using a namespace + class scope or ctypes to replace dict methods
Hmm, are you telling me that rather than dis ing the calling scope, there's an easier way with with an __iter__(), __enter__() and __exit__()
class Deconstruct(_):
a, b, c = _({"a":6, "c":4, "b":3})
should be possible
given _ has a metaclass which defines __call__ and __prepare__
Heh, a challenge would be to make that work with a one-line (no cheating) _ = ... definition
calling forth all python villains!
Oooh I was unaware of __prepare__ reading up on it now.
Yeah you can basically customize everything with the whole namespace there
I'm not sure you understand what I'm trying to do. Though I'd find it totally believeable that there's a way to get the normal call by named parameter mechanism to do what I want.
very easy to implement with https://github.com/pwwang/python-varname
something like
def deconstruct(mapping):
return [mapping[name] for name in varname.varname(multi_vars=True)]
not sure how well it can handle with specifically
in with you can use the frame to get the line of code and regex out the names
ah i see, you want to mutate the values using the vars too
in that case youd just return some proxies with operator overloads
you know how you can do
li = [1,2,3,4]
a, b, c, d = li```
and it behaves similarly to ```py
li = [1,2,3,4]
def func(a,b,c,d):
print(a,b,c,d)
func(*li)```
I have an implementation of deconstruct() so that:
```py
d = {'a':1, 'c':3, 'd':4, 'b':2}
a, b, c, d = deconstruct(li)```
behaves similarly to:
```py
def func(a,b,c,d):
print(a,b,c,d)
func(**d)```
I'm contemplating how I'd implement a feature such that: py d = {'a':1, 'c':3, 'd':4, 'b':2} with deconstruct({'a':1, 'c':3, 'd':4, 'b':2}) as c,b,a: a+=1 b-=1 c*=2 print(d) not only deconstructs as intended, but also saves the ordering & vars, and a reference to the original dict, and at __exit__() repacks them from the calling namespace back into the dict. etc.
varname looks cool!
The executing library (https://pypi.org/project/executing/) that varname uses looks like a more professional version of how I implimented.
ohh nice
Once I remembered that with requires parenthesis around the return tuple, it was fairly straightforward: ```py
@contextmanager
def Destructure(di):
values, varnames = destructure(di, 2,#<--skip one frame for Destructure() one frame for contextmanager()'s helper function
report_varnames=True)
try:
yield values
finally:
frame_locals = sys._getframe(2).f_locals
for varname in varnames:
di[varname] = frame_locals[varname]
def test():
di = {'d':1, 'c':2, 'b':3, 'a':4, 'g':0}
print(di)
with Destructure(di) as (a,b,c,d):
print(a,b,c,d)
a+=1
b-=1
c*=2
print(a,b,c,d)
print(di)
test()
And I had to add the named parameter report_varnames to the destructure() function, but all the data was there, I just had to return it so I could use it later.
You could hook dict.__enter__ and dict.__exit__ so you can just do with di as (...,): ...
challenge:
a, *b = "test" # "t", "est"
*_, c, d = {"a": 5, "c":0, "d":1, "b":2} # {"a":5,"b":2}, 0, 1
What?
the most tricky part of that challenge is setting _ to be a dict instead of a list
because UNPACK_EX hardcodes a list (it calls PySequence_List)
how do you even go about checking that?
I look at the cpython source code
__annotations__ = globals();
x: 5 = int;
stdout: open(1, 'w');
number: __import__('random').randint(1, 5) = int;
for function in [type]:
Main = function("Main", (), {
'__init__':lambda self:{
(x == 5) & stdout.write(str(x)),
(x <= 10) & stdout.write(__import__('codecs').decode(b'\x48\x65\x6c\x6c\x6f\x20\x77\x6f\x72\x6c\x64\x21')),
({1:True, 2:True}.get(number, False)) & stdout.write(__import__('codecs').decode(b"\x4e\x69\x63\x65\x21"))
} and None,
'Start' :(lambda*self,: (()in((),),((),)in()))(),
'_':0==0})
y = int;
if (y := 12): {(lambda SubMain: stdout.write(str(SubMain)))(Main().Start)};
``` reviving my old project, anyway of making this more esoteric?
what in the goddamn fuck - this is much worse
@floral meteor did you find a way to get the integer cache shuffling working?
I can write something to shuffle the numbers
The trick is keeping python from crashing
yeah
I don't think you can change 0 or 1
@earnest wing https://github.com/chilaxan/pysnippets/blob/main/dict_destructure.py ```py
x = {'a':1, 'b': 2, 'c':3}
a, b, *_ = x
a, b, _
(1, 2, {'c': 3})
I believe it covers all of the edge cases
heck yeah
just fixed one bug that i noticed, should be good now
(if you had an invalid name after the unpack arg, the unpack arg would still be set)
now that doesnt happen
if you're curious how https://github.com/chilaxan/pysnippets/blob/main/dict_destructure.py#L44-L45 works, look at f_locals.py it enables the locals dictionary to be writable
dict_destructure.py lines 44 to 45
elif op_name == 'STORE_FAST':
frame.f_locals[ex_name] = copy```
@floral meteor ^ success
That is impressive. f_locals.py and fishhook are based
is there like a site i could read that has short obscure python snippits and explanations for beginner-intermediates?
like id never seen the thing in the channel description >>> (lambda: 'x')() # 'x' before
A lambda function is just a normal python feature, that snippet there is a bit like doing
def func():
return "esoteric python"
func()
see https://www.programiz.com/python-programming/anonymous-function
for obscure python behaviour you could look at https://github.com/satwikkansal/wtfpython
if you wanted to write that as a lambda function it would be func = lambda: "esoteric python"
ya word i just didnt know you could evaluate it instantly like in js (()=>'x')() thanks for the link
ah yeah, it's an expression rather than a statement so you can do whatever you want with it
like (lambda _:_()())(lambda : lambda: print('lol'))
or put it in a print and do print((lambda _:_()())(lambda : lambda: 'Hello') + ' World!')
that is lol
@earnest wing any more challenges? the dict_destructure was fun
@rugged sparrow
>>> x = 4
>>> y = --x
>>> y
3
>>> --x
2
>>> ++++x
4
>>> ++4
4
>>> print(x, -(---x))
4 3
>>>
Made a mistake lol, edited
so --x == (x := x - 1) in functionality?
see this gets tricky because -(---x) is the same as ----x after compliation
Just go with the simple -- and ++
yes
alright that's optional
I might instead specify```py
x = 4
x = -x
x = -x
x # ensure two consecutive negatives don't incorrectly decrement
4
x = --x # name assign
x
3
--x, x # java decrement behaviour
(2, 2)
print(++x, ++x) # ensure precise stack edits
3 4
y = --x # java decrement behaviour
x, y
(3, 3)
---y # negative decrement y
-2
----y, ++++x # this should double decrement/increment
(0, 5)
--(1 + x), x # this shouldn't mutate x
(6, 5) # should behave normally in this case
@rugged sparrow updated specs
sounds good
meanwhile I'm gonna exercise these skills on a hopefully easier task: goto, where labelling is done with and stored in annotations
damnit just found a major bug in dict_destructure.py
if only this worked ```py
from future import annotations
import sys
class Annotations(dict):
def setitem(self, name, value):
if name.lower()=='label':
super().setitem(value, sys._getframe(1))
elif name.lower()=='goto':
if value in self:
target_frame=self[value]
current_frame=sys._getframe(1)
current_frame.f_lasti=target_frame.f_lasti ;# prowgremenng
else:raise NameError(value)
else:raise SyntaxError(f"Unkown token: {name}")
annotations = Annotations();
c = 0
i = import('random').randint(3, 99999);
Label: hither;
print (i, 'incomplete');
if i > 2:
i = 3*i+1 if i%2 else i//2;
c += 1
Goto: hither;
print ('completed', c, 'iterations');
voodoo magic
but i can't voodoo it with a function because annotations and locals behave differently to the global namespace
otherwise i'd make a function, paste the frame's remaining code, voodoo it with the above frame, then terminate the program
!e this is a little less customisable than _sitebuiltins.Quitter```py
print('before abortion')
import('os').abort()
print('after abortion')
@floral meteor :x: Your eval job has completed with return code 139 (SIGSEGV).
before abortion
f_locals.py line 20
self._r = True```
oh its for the recursive repr
if self._r:
...
self._r = True
then it isnt used, then it's set to False
but it already has to be truthy to get there
and in most cases, truthy is as good as True
https://github.com/chilaxan/pysnippets/blob/d28649171c3861dd75eea50afceab542d9d888ad/f_locals.py#L22 this line will call self.__repr__() if locals contains itself (so self._r will be truthy and will instead return '...')
f_locals.py line 22
return f'locals({dict(self.items())})'```
@floral meteor ^
oh i see
lol i got it the wrong way around
thats why theres computer parsing these if statements, not me
!e print(import('dis').opname)
@floral meteor :white_check_mark: Your eval job has completed with return code 0.
['<0>', 'POP_TOP', 'ROT_TWO', 'ROT_THREE', 'DUP_TOP', 'DUP_TOP_TWO', 'ROT_FOUR', '<7>', '<8>', 'NOP', 'UNARY_POSITIVE', 'UNARY_NEGATIVE', 'UNARY_NOT', '<13>', '<14>', 'UNARY_INVERT', 'BINARY_MATRIX_MULTIPLY', 'INPLACE_MATRIX_MULTIPLY', '<18>', 'BINARY_POWER', 'BINARY_MULTIPLY', '<21>', 'BINARY_MODULO', 'BINARY_ADD', 'BINARY_SUBTRACT', 'BINARY_SUBSCR', 'BINARY_FLOOR_DIVIDE', 'BINARY_TRUE_DIVIDE', 'INPLACE_FLOOR_DIVIDE', 'INPLACE_TRUE_DIVIDE', '<30>', '<31>', '<32>', '<33>', '<34>', '<35>', '<36>', '<37>', '<38>', '<39>', '<40>', '<41>', '<42>', '<43>', '<44>', '<45>', '<46>', '<47>', 'RERAISE', 'WITH_EXCEPT_START', 'GET_AITER', 'GET_ANEXT', 'BEFORE_ASYNC_WITH', '<53>', 'END_ASYNC_FOR', 'INPLACE_ADD', 'INPLACE_SUBTRACT', 'INPLACE_MULTIPLY', '<58>', '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',
... (truncated - too long)
Full output: https://paste.pythondiscord.com/vozenosike.txt?noredirect
https://github.com/chilaxan/pysnippets/blob/main/goto.py @floral meteor take a look
now which one would i use to jump to an instruction number?
'JUMP_FORWARD': 110, 'JUMP_ABSOLUTE': 113
!e print(import('dis').opname[114])
@rugged sparrow :white_check_mark: Your eval job has completed with return code 0.
POP_JUMP_IF_FALSE
How do you know all of this?
Did you just figure everything about this in your free-time
yea
whenever someone says xyz cant be done in python i feel a need to prove them wrong
and that lead to me learning a ton of stuff
You feel a need to prove them wrong?
Then you can't add an operator of your choice in Python
doesn't matter the functionality
see i know how to do that at the C level
but at the python level you need to use a preparser and that feels like cheating
How would you do it at the C level
edit the source code
modify the grammar file
well it can be done at the python level but then it sort of becomes making an interpreter of another language, and integrating it into python
also by the time your code is running the parser isnt running anymore (except for eval or exec) so its moot
you can do the hooks but they will never get called because python is done parsing your code
Interesting
How about trying to implement features and syntax from other languages
that sounds fun to me
so the python syntax override engine would look like
from hackery_repository import hack_python_itself
from specs import data, main
hack_python_itself(data)
import victim
main(victim)
where specs is the code to run and must specify data including the syntax to override, and a main function, and hack_python_itself has the dirty stuff
see but all this is like replacing the door after you entered the house with goal of the door being changed before you entered
although this becomes using python as an engine to modify a python interpreter, only it's itself
unless you delayed imports but if youre doing that you can just use an import hook to call a preparser
although with quantum computing...
maybe we could affect an anti-particle to go back in time and preparse the code
modifying the python grammer inplace is a very complicated problem, involving getting addresses of unexposed functions, modifying their assembly (or adding jmps to new assembly) and it has no purpose, because there are easier and more effective ways to solve the problem (preparser)
@rugged sparrow is it okay if i plagiarise your code to make an annotations implementation?
what's op 106?
should i replace it with 85 for annotations?
!e ```py
"""
adapted to annotations from https://github.com/chilaxan/pysnippets/blob/main/goto.py
"""
from future import annotations
from ctypes import c_char
def getmem(addr, size):
return memoryview((c_char*size).from_address(addr)).cast('B')
all = ['goto', 'label']
def getframe(depth=0):
try:raise
except Exception as e:
frame = e.traceback.tb_frame
for _ in range(depth + 1):
frame = frame.f_back
return frame
def get_dest(frame, label):
code, names = frame.f_code.co_code, frame.f_code.co_names
ops, args = code[::2], code[1::2]
for idx, (op, arg) in enumerate(zip(ops, args)):
try:
if names[arg-2]=='Label'and names[arg]==label:return(idx-1)*2
except:pass
raise RuntimeError(f'label {label!r} not found')
def set_instr(code, idx, op, arg):
code_addr = id(code) + bytes.basicsize - 1
mem = getmem(code_addr, len(code))
(op, arg), mem[idx:idx + 2] = mem[idx:idx + 2], bytes((op, arg))
return op, arg
restore_instr = [None, None, None]
class annotations(dict, metaclass=lambda*a:type(*a)()):
def setitem(self, token, name):
if token.lower()=='goto':
code = (frame:=getframe(1)).f_code.co_code
idx = frame.f_lasti + 2
op, arg = set_instr(code, idx, 114, get_dest(frame, name))
restore_instr[:] = idx, op, arg
elif token.lower()=='label':
frame = getframe(1)
if None not in restore_instr:
set_instr(frame.f_code.co_code, *restore_instr)
restore_instr[:] = None, None, None
super().setitem(name, frame)
elif token.lower()=='rem':pass
else:raise SyntaxError(f'Unknown token: {name}')
c = 0
i = import('random').randint(3, 99999);
REM: these-bloody-batch-files
Label :hither
print (i, 'incomplete');
if i > 2:
i = 3*i+1 if i%2 else i//2;
c += 1;
Goto :hither
print ('completed', c, 'iterations');
@floral meteor :x: Your eval job has completed with return code 1.
001 | 27607 incomplete
002 | Traceback (most recent call last):
003 | File "<string>", line 58, in <module>
004 | File "<string>", line 39, in __setitem__
005 | File "<string>", line 23, in get_dest
006 | IndexError: tuple index out of range
the virgin JUMP_FORWARD vs the chad JUMP_ABSOLUTE
"pycharm doesn't like a lot of my perfectly reasonable code" - this place
Can you run flask and ursina at the same time
Because they both use a app.run
getting somewhere slowly. gonna try to make the lexer treat backticks as single quotes with gdb
building up more familiarity with the parser's workings by messing around with it
I see
glad it doesnt tbh
I have implemented Rust-like traits in python, and I'm working on ast macros
Wanna show it?
@floral meteor ```py
x = 5
while --x:
... print(x)
...
4
3
2
1```
currently supports globals and locals
i want to update later to support ++x[0] or ++x.a but that requires some more work
@tribal moon ^ implemented prefix ++ and --
finding the fastest way to unpack an iterable
step 1: scope the grounds
step 2: know why
step 3: improvise
step 4: recognize your failures when they approach you
fun
If you can figure out how to override an operator in Python, a good exercise would be to implement the ternery operator
sounds like it'd be fun for you
oo ternary is tricky
good luck!
we need to decide the ternary syntax
how's this? ```py
(condition @ T) << if_true | if_false
T is the ternary class
__code__.replace(
__import__('types').CodeType(...)
```is easier
!e ```py
class :
def init(_,):_.=;.=None
def lshift(,漢):.___=漢 if ._ else .;return _
def or(,漢):return 漢 if ._____-1 else .;
class :
def rmatmul(,):return __()
T=_()
print((1 > 0) @ T << 'True!!' | 'False!')
print((1 < 0) @ T << 'True!!' | 'False!')
@dawn kayak :white_check_mark: Your eval job has completed with return code 0.
001 | True!!
002 | False!
this seems to work eh
is this good for you?
no it isn't, I'd have to provide all the other fields then
Exactly - you learn more!
there's no learning in forwarding the same values you were given
also different python versions can have different parameters for CodeType. Using replace means you dont have to use different calls to CodeType per python version if youre not altering version dependent parameters.
e.g. 3.8 added posonlyargcount
Portability is for the weak.
You could hook object.__matmul__ to make it work without T
Yeah that works
But the real challenge is to make something like 1 > 0 ? True : False
you'd need to make 2 new operators ? and :
if you make ? return a specific sentinel and : checks for that sentinel, it shouldn't be too bad
If it matters, ? isn't valid Python.
Is there a good way to hook custom types instead of *args and **kwargs? e.g. if i wanted to pass a Counter instead of kwargs.
e.g.
@kwarg_class(Counter)
def foo(**kwargs):
assert isinstance(kwargs, Counter)
with CPython hacks, of course
As I mentioned, I have a problem about web scraping to get dataset. import pandas as pd df = pd.read_html('http://www.olympedia.org/editions/61/medal') df[0] My problem are defined below.
1 ) As you can see, main sport event is composed of all columns. How can I add it at the end of column named for "Main Sport/Event".
2 ) As I mentioned before, multi athletes appear in bronze column of some sport events. How can I fix it?
I hope anyone can help me. I'm looking forward to waiting your response.
So you would want calls to foo(x=1, y=2) to be autoconverted to foo(Counter(x=1, y=2))?
No, inside the function, kwargs should be not a dict, but a Counter
Yea you can do that by changing the code flags on foo and then wrapping it
!e ```py
def kwargs_type(typ):
def wrapper(func):
code = func.code
func.code = code.replace(
co_flags=code.co_flags ^ 0x0008,
co_argcount = code.co_argcount + 1
)
def wrapped(*args, **kwargs):
return func(*args, typ(**kwargs))
return wrapped
return wrapper
from dataclasses import dataclass
@dataclass
class Foo:
a: int
b: int
@kwargs_type(Foo)
def foo_func(a, b, **kwargs):
print(a, b, kwargs)
foo_func(1, 2, a=3, b=4)
@rugged sparrow :white_check_mark: Your eval job has completed with return code 0.
1 2 Foo(a=3, b=4)
@formal sandal ^ not to bad
okay i didn't know max had a default kwarg, that might come in handy for golfing
(function)
max(__arg1: SupportsLessThanT@max, __arg2: SupportsLessThanT@max, *_args: SupportsLessThanT@max, key: None = ...) -> SupportsLessThanT@max
max(__arg1: _T@max, __arg2: _T@max, *_args: _T@max, key: (_p0: _T@max) -> SupportsLessThan) -> _T@max
max(__iterable: Iterable[SupportsLessThanT@max], *, key: None = ...) -> SupportsLessThanT@max
max(__iterable: Iterable[_T@max], *, key: (_p0: _T@max) -> SupportsLessThan) -> _T@max
max(__iterable: Iterable[SupportsLessThanT@max], *, key: None = ..., default: _T@max) -> (SupportsLessThanT@max | _T@max)
max(__iterable: Iterable[_T1@max], *, key: (_p0: _T1@max) -> SupportsLessThan, default: _T2@max) -> (_T1@max | _T2@max)
max(iterable, *[, default=obj, key=func]) -> value max(arg1, arg2, *args, *[, key=func]) -> value
With a single iterable argument, return its biggest item. The default keyword-only argument specifies an object to return if the provided iterable is empty. With two or more arguments, return the largest argument.
https://github.com/chilaxan/pysnippets/blob/4535d870bbb3dab8c6db2de0baf8fd474e99032c/memview_fromaddr.py#L7 @rugged sparrow where's memoryview defined?
import gc, ctypes
def find(obj, typ):
for o_obj in gc.get_objects():
if isinstance(o_obj, typ) and obj==o_obj:
return o_obj
mp_dict = find(memoryview.__dict__, dict)
def getmem(addr, size):
return (ctypes.c_char*size).from_address(addr)
@classmethod
def from_address(cls, addr, size, fmt='c'):
return cls(getmem(addr, size)).cast(fmt)
mp_dict['from_address'] = from_address
memview_fromaddr.py line 7
mp_dict = find(memoryview.__dict__, dict)```
wait that's a builtin?
Yea
That file gives memoryview a convenience method for making one that points to arbitrary memory
im deciding what my brainfuckery of the week is going to be
!e ```py
def args_type(atyp=None, kwtyp=None):
def wrapper(func):
code = func.code
flags = code.co_flags
oldargcount = argcount = code.co_argcount
if atyp is not None:
flags ^= 0x0004
argcount += 1
if kwtyp is not None:
flags ^= 0x0008
argcount += 1
func.code = code.replace(
co_flags = flags,
co_argcount = argcount
)
def wrapped(*args, **kwargs):
nargs = args[:oldargcount]
return func(
*nargs,
*[atyp(args[oldargcount:])] if atyp is not None else [],
*[kwtyp(kwargs)] if kwtyp is not None else []
)
return wrapped
return wrapper
from dataclasses import dataclass
@dataclass
class Foo:
a: int
b: int
@classmethod
def from_dict(cls, dct):
return cls(**dct)
@args_type(list, Foo.from_dict)
def foo_func(*args, **kwargs):
print(args, kwargs)
foo_func('a', 'b', a=1, b=2)```
@rugged sparrow :white_check_mark: Your eval job has completed with return code 0.
['a', 'b'] Foo(a=1, b=2)
in case you wanted to control *args type as well
!e ```py
from future import barry_as_FLUFL
exec("print(3 <> 4)")
@floral meteor :white_check_mark: Your eval job has completed with return code 0.
True
!e ```py
from ctypes import*
py_object.from_address(0).value = 69
@floral meteor :warning: Your eval job has completed with return code 139 (SIGSEGV).
[No output]
!e ```py
from ctypes import*
*pointer(py_object.from_address(id(1))),
@floral meteor :warning: Your eval job has completed with return code 139 (SIGSEGV).
[No output]
a few easy segfaults
!e py import ctypes;ctypes.string_at(0)
@rugged sparrow :warning: Your eval job has completed with return code 139 (SIGSEGV).
[No output]
This doesn't work on windows
it gets caught and raises OSError instead
memory access violation at 0x00000000000000000000000
zero-x-holddownthezerokeyforfiveseconds
nah sizeof from ctypes reports the size of the memory a c_object references. so c_int -> 4 but byref returns a python object not a c_type
how do i convert py to a exe looked one yt it doesent show in the dist folder
it does work. That OSError is just windows' idiot version of a segfault
thats not the error code it gives, thats the address
yes?
that's memory access violation
that's different to windows behaviour using string_at(0)
string_at(0) raises a python error and doesn't crash it
py_object.from_address(0).value crashes python and registers a critical event