#esoteric-python
1 messages · Page 85 of 1
it creates the missing value based on the missing key
isn't the use case for that really small
and you'd be better off using setdefault probably?
what's that?
but the point is that the missing value is created dynamically based on what the missing key is.
this appears to return a constant.
can't you just do .setdefault(n, func(n)) or something
I don't find that as enjoyable to read as how the class I designed would look in action.
for the same reason that defaultdict exists.
enjoyable to read? sure. enjoyable to use? well with your dynamic dict, you need to import stuff, write a new class, and use that
it seems a bit contrived
that's why I want it to be integrated into the language.
who would ever actually use this though
I'm not sure that that's a fundamentally different question than asking why anyone would use defaultdict
it would be another streamlined solution for the handling of missing keys.
Is there anything in #esoteric-python that's deliberately easy to use or practical, though?
I only asked about it here because my initial comment was about my efforts to implement the class I want in the C code, and I didn't think that would be a popular topic in the general chat.
@formal sandal yeah but this guy wants it in the standard library
Can't you just use lru_cache or something?
My implementation compiles, but then from collections import dynamicdict raises ImportError at runtime.
I don't know that I fully understand what brings C code into something that's usable in Python.
do import collections print(collections) just to check that it's pointing to the right version I guess
yes, it's the right version.
I'm running the repl for the python version in my cpython clone.
I still don't really get why you need it, but it's odd that that doesn't work
I was working on a project a few days ago and was surprised that defaultdict(lambda key: make_something(key)) isn't a thing.
which I guess shouldn't be too horribly surprising.
I mean d.setdefault(key, make_something(key)) is a thing
but ok
lol
also defaultdict(lambda key=None: make_something(key))
that is a thing
which works
oh nvm it isn't
well yeah of course it isn't, a lambda is just a function, and when you use a defaultdict you don't pass the key to the function
like if you do d = defaultdict(int), when you do d[5] you don't expect it to set it to int(5)
Hey @fiery hare sorry for delay, now looking at your code
there are some small things that come to my eye, but other then your code looks OK. I think you are missing to add dydict_type to module state which should be end of the module
@bitter iris oh, let me look at that
here
an example would be this
defdict_type.tp_base = &PyDict_Type;
if (PyType_Ready(&defdict_type) < 0)
return NULL;
Py_INCREF(&defdict_type);
PyModule_AddObject(m, "defaultdict", (PyObject *)&defdict_type);
just replace defdict with your dydict
thanks! I'm recompiling it now.
huh, apparently my pure Python implementation doesn't actually save the value in the dict; it just returns it.
so that's a problem.
are you planning to propose this feature or adding for just for fun and exercise?
definitely for fun/exercise
I would write a PEP but I don't know if that would go over well.
this probably doesn't deserve a pep but there will be a hot dicussion around the idea because it is relatively big thing
>>> from collections import dynamicdict
>>> dynamicdict
<class 'collections.dynamicdict'>
>>> bob = dynamicdict(lambda x: x.upper())
>>> bob['hi']
Segmentation fault
idk what I expected.
oh, a defaultdict that uses single argument lambdas?
I think I know what line of C code is the problem though
@zealous widget that's the idea. so the missing value is created based on the new key.
I did
defining the class in pure Python makes the runtime of import collections way way slower.
@fiery hare just throw it to gdb and find where it crashes
I'm pretty sure that's where it crashes because that was the line where I guessed.
oh I see
I initialized the tup as a PyObject pointer, but it only gets a value in that one if statement.
but I'm not sure if having NULL in place of the kwargs argument that it wants is correct.
I'm also upset with myself that I made a commit in which I had a commented-out line.
Python 3.8.2+ (heads/3.8-dirty:55a0efb, Apr 9 2020, 15:01:06)
[GCC 5.4.0 20160609] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from collections import dynamicdict
>>> bob = dynamicdict(lambda x: x.upper())
>>> bob['hi']
'HI'
>>> bob['hello'] += ' world'
>>> bob['hello']
'HELLO world'
omg it worked

let's see if I can break it
was this your python version:
class DynamicDict(dict):
def __init__(self, func, *args, **kwargs):
self.func = func
super().__init__(*args, **kwargs)
def __getitem__(self, key):
if key not in self:
super().__setitem__(key, self.func(key))
return super().__getitem__(key)
that's the idea, yes
i wonder if i can make it better with get
get?
no, that's not a good idea
i'm gonna keep this class around, i'll probably find a use for it somewhere
you won't have to if I demand that Guido and friends integrate it into the standard library.
though I don't anticipate that happening.
yeah, it seems similar enough to defaultdict that it wouldn't be too much effort to include it
could just allow defaultdict to accept single or no argument functions
well, that might get confusing if you pass int or something
yeah, I think they'd have to change how defaultdict is implemented.
I saw in the C code that presently, the C function that causes the factory method to be called in the Python code is hard coded to be an argument-less function call.
I wish I knew a better way to make the dynamicdict implementation less repetitive.
i know nothing about c internals
they're something.
I have to hop off but thanks @zealous widget @bitter iris @acoustic zinc
What are you using the dynamicdict for?
@rak1507#1964 you could use it for a lot of things.
oh looks like rak left the server.
oh he was banned.
ok so
i might regret this
how can i compact this, while still being able to understand it
xD
actually nvm i redacted that
iirc defaultdict has an overridable __missing__ magic method that you could use to implement this too @zealous widget @fiery hare
ah already mentioned
nevermind
didnt scroll up far enough
@brazen geyser the missing method doesn't cause the new key to be saved.
you can implement it to do such
speed vs portability
Portability?
if someone wants to use your c implementation of dynamicdict theyll need either a c extension or your specific python build
whereas with a pure python approach there'll be no additional dependencies
i thought by 'that runtime though' you were referring to runtime overhead
which, yeah, pure python implementation will have a significant amount of compared to a c implementation
or did i misunderstand?
I don't think you did.
However I'm considering proposing that it get integrated into the standard library
oh cool
personally i think it might make more sense as an addition to defaultdict, rather than a separate class
but not sure of what hidden performance gotchas there could be for that
do any stdlib modules use defaultdicts? that could probably be the number 1 case against a change to defaultdict
The c implementation of defaultdict doesn't let the factory method have parameters.
the proposal would cover that presumably
So change defaultdict so that it supports both?
maybe it could be a classmethod. defaultdict.fromcallable
yeah
at a glace it seems fairly redundant to make an entirely new class for this
Defaultdict always uses a callable
It just doesn't pass any arguments to it.
Yes, it's very repetivie.
Repetitive
Only a few lines are different
right, so the classmethod would give you a specific type of defaultdict which does that
I'd have to see how you can implement a class method directly in C.
defaultdict.keybased? better name?
I don't think that would change anything about how it gets implemented in C.
to fix the repetitiveness issue you could probably like, swap in a function pointer somewhere to differentiate between normal and key base default dict behaviour. but... as im typing it out it's starting to seem like a worse and worse idea 😅
yes, i mean it might be more accurate than defaultdict.fromcallable for example
more descriptive
The important part in the C code is that apparently, the way C causes functions to be called in Python is that it passes the callable and the arguments to a C function
And the C function can't take any arguments other than the callable itself
@brazen geyser I changed the source code for defaultdict (rather than having the other class) so that you can have either behavior, but you still have to give it a callable that takes one argument, even if it doesn't use it
because otherwise if you supply a callable that can run with or without an argument, it can't know when you don't want it to pass that argument to the callable.
I don't think that would go over well.
basically it has to be a separate class or the new implementation of defaultdict wouldn't be backwards compatible.
how do you subclass in c
@zealous widget you can't because there aren't classes, but I assume there's some concept of extending the behavior of code that already exists.
I found my one C course agonizing.
And next year during my last semester I have to write an OS emulator, which probably means a return to that realm.
in C, defaultdicts are a struct that contain a pointer to a dict and a pointer to a defaultfactory
problem is, dynamicdict doesn't extend the behavior of defaultdict as much as it changes the behavior of the extension.
>>> factorial = func('x<2', 1, 'True', 'x*factorial(x-1)')
>>> factorial(5)
120
I made this thing
def func(*args):
def a(x):
for condition, returnarg in zip(args[::2], args[1::2]):
if eval(condition):
if type(returnarg)==int:
return returnarg
elif type(returnarg)==str:
return eval(returnarg)
else:
return returnarg(x)
return a
this code is very stupid but it works kind of
I probably should've made this a class but whatever
@gilded orchid make it a class that only has a static call method
Why would it be a class?
@formal sandal to make it more esoteric
I thought lambdas make things more esoteric
They can.
There's enough esoteric to go around.
Is it conceivably possible to do anything in python with one statement
Yes.
Exception handling is difficult, but beyond that.
exec("""your code here""")
@zealous widget the first person to comment on my idea wasn't a fan.
Asyncio is a pain to do in one expression
*laughs in JS*
@gilded orchid ah btw, little tip — type(x) is to be better compared using is, e.g. type(x) is int.
Since you're going to get the class back as a result, there's only one version of any given class. So == is rather redundant.
I just use isinstance().
yeah, definitely isinstance
That said, isinstance(True, int) so sometimes type(x) is y is necessary.
!e
is_proper_int=lambda x:{*x.__class__.__mro__}&({bool}|{int})&{int,bool}==({*(([([([])])])in()).__class__.__mro__}-{bool,object})
print(is_proper_int(True))
print(is_proper_int(42))
print(is_proper_int(0.5))
@formal sandal :white_check_mark: Your eval job has completed with return code 0.
001 | False
002 | True
003 | False
Slightly obfuscated, but you get the idea.
Just a little.
So this is an obfuscated way of determining of an object is an int and only an int?
@fiery hare It checks that an object is an int and not a bool.
So it will work with int subclasses.
ok... very interesting
def f(x):
if x < 2**2**20:
return -1
return x
f.__code__.co_consts
Out[4]: (None, 2, 1048576, -1)
it will cache 2 and the result of 2**20, but not the result of 2**2**20.
¯_(ツ)_/¯
at what point does it decide not to evaluate a constant expression?
write a loop and try it out
>>> def f():
... "a"*5
... "a"*21
...
>>> f.__code__.co_consts
(None, 'aaaaa')
I have no idea about ints but it skips sequences after 20
or something like that
if it's multiple + chained together it's cached properly
2 0 LOAD_CONST 1 (6)
2 STORE_FAST 1 (a)
3 4 LOAD_FAST 0 (x)
6 LOAD_FAST 1 (a)
8 COMPARE_OP 0 (<)
10 POP_JUMP_IF_FALSE 16
4 12 LOAD_FAST 0 (x)
14 RETURN_VALUE
5 >> 16 LOAD_CONST 2 (-1)
18 RETURN_VALUE
hmm that seems to be a side effect of hwo I wrote it, that limit could be just for the basic interning but it does skip them on a few thousand
it makes perfect sense to skip at some point since the fact is that evaluating those constants in itself is a full program run
unless there are limitations to what it even tries to cache
"A"*calculate_the_next_largest_prime()
hmmm
weird
def f(x):
if x < 2**65:
return x
f.__code__.co_consts
Out[57]: (None, 2, 65)
def f(x):
if x < 2**64*2:
return x
f.__code__.co_consts
Out[55]: (None, 36893488147419103232)```
should i try power of 3?
I don't know if I'm correct but it would make sense since bitshifts are almost free
anything of the form 2**k should be able to cache\calculate easily
positive or negative exponent
but though
probably?
def f(x):
if x < 3**64:
return x
f.__code__.co_consts
Out[74]: (None, 3433683820292512484657849089281)
def f(x):
if x < 3**65:
return x
f.__code__.co_consts
Out[76]: (None, 3, 65)```
try 63
4**42 is the highest it will cache
but you can then just do *4 *4 ...
now this is very weird
Python 3.8.0 (default, Oct 16 2019, 20:51:08)
[GCC 7.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> f = lambda:0
>>> f.__code__ = f.__code__.replace(co_consts=())
>>> f()
Segmentation fault (core dumped)```
lol
def f(x):
if x < 4**42 * 4**42:
return x
f.__code__.co_consts
Out[96]: (None, 19342813113834066795298816)
math.log(19342813113834066795298816, 4) # log4(...)
Out[97]: 42.0
4**42 * 4**42
Out[98]: 374144419156711147060143317175368453031918731001856
math.log(374144419156711147060143317175368453031918731001856, 4)
Out[99]: 84.0```
not like python cant handle that value
oh ofc it only has to cache it once
lol
def f(x):
if x < 4**42 * 4**41:
return x
f.__code__.co_consts
Out[103]: (None, 19342813113834066795298816, 4835703278458516698824704)```
then it caches both
my bad
[(f:=lambda:0).__setattr__('__code__',f.__code__.replace(co_consts=())),f()]
what's happening there
it creates an invalide code object that segfaults python
but theoretically?
its prob a python bug
co_consts needs to contain None
but i set it to an empty tuple and it doesnt verify against it
it creates a lambda function which gets "named" f, then the __code__ is set to an empty tuple, and then you call the function
no the __code__ is set to a modified code object
yeah
you're taking the copy of the same __code__ and putting it back in place but replacing the co_consts this time
so if you set it to None - this works?
it returns 0 in the list comprehension's last element?
>>> [(f:=lambda:0).__setattr__('__code__',f.__code__.replace(co_consts=(None,))),f()]
[None, 'exit']```
right because the 0 isn't there anymore
if it ever even was...?
not sure if the lambda gets cached?
well it didn't return 0 for sure
but I thought that was because the co_consts overrode it
what version of python are you running?
Python 3.8.2 (tags/v3.8.2:7b3ab59, Feb 25 2020, 22:45:29) [MSC v.1916 32 bit (Intel)] on win32
isnt most of this channel messing with python in ways you shouldnt?
wait @marsh void did you implement braces using something like brm?
yeah I mean
you might try to break it
it does not work with py def main() { print('ew'); }
https://github.com/NeKitDS/braces.py/blob/master/braces/token_transform.py here is the main thing that does the work
np
have fun ig
I need to work with untokenizing a bit
current goal is to make it pass flake8 after converting
>>> [(f:=lambda:0).__setattr__('__code__',f.__code__.replace(co_consts=(None,))),f()]
[None, <
object at 0x000001D413648730>]```
>>> res = _[1]
>>> res
<
object at 0x000001D413648730>``` wat
>>> res
<
object at 0x000001D413648730>
>>> type(res)
<
object at 0x000001D4134B0AB0>
>>> type_res = _
>>> res
<
object at 0x000001D413648730>
>>> type_res
<
object at 0x000001D4134B0AB0>
>>> type(type_res)
< object at 0x000001D4134B0AE0>
>>> type(type(type_res))
< object at 0x000001D41363DE40>
>>> type(type(type(type_res)))
< object at 0x000001D41363DDB0>
>>> type(type(type(type(type_res))))
< object at 0x000001D41363DD20>
>>> type(type(type(type(type(res)))))
< object at 0x000001D41363DD20>
>>> type(type(type(type(type(type(res))))))
segfault```
Lmfao
this is similar to the deallocated tuple stuff except the object is a bit more stable
is .replace really a thing?
yea
in 3.8 it is
also i found the reason it does that
>>> import dis
>>> dis.dis(lambda: 0)
1 0 LOAD_CONST 1 (0)
2 RETURN_VALUE
>>> (lambda: 0).__code__.co_consts
(None, 0)```
lol
also prob construct valid bytecode that points to a const that doesnt exist
huh, this is very interesting
the result of [(f:=lambda:0).__setattr__('__code__',f.__code__.replace(co_consts=(None,))),f()] changes depending on what you've done before
it's deterministic but unpredictable
welcome to the world of undefined behavior
>>> open()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: open() missing required argument 'file' (pos 1)
>>> [(f:=lambda:0).__setattr__('__code__',f.__code__.replace(co_consts=(None,))),f()]
[None, 'quit']
>>> [(f:=lambda:0).__setattr__('__code__',f.__code__.replace(co_consts=(None,))),f()]
[None, 'copyright']
>>> 1/0
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ZeroDivisionError: division by zero
>>> [(f:=lambda:0).__setattr__('__code__',f.__code__.replace(co_consts=(None,))),f()]
[None, <
object at 0x000002512ABC8730>]```
by making my lambda:0 load a const from index 9 i got it to make a new empty tuple (normally not possible without weird ctypes)
sys.getrefcount(x())
1
>>> x()
((...), ('x',))
>>> x()
((...), (((<NULL>,),),))
>>> x()
[1] 2633 segmentation fault python3.8
well that's not an empty tuple but good job anyway
whatever that is, it's... very weird
oh it was an empty tuple originally
not entire sure how it changed
>>> r = lambda f, **kwargs:setattr(f,'__code__',f.__code__.replace(**kwargs))
>>> x = lambda:0
>>> r(x,co_code=b'd\x09S\x00')
>>> x()
(None,)
>>> x()
('x',)``` uh
>>> r(x,co_code=b'd\x10S\x00')
>>> x()
(((<NULL>, b'd\x10S\x00'), ('x',)), 'x')
>>> 1
1
>>> x()
(((((((((...), (...)), ()), ((<NULL>,),)), ((((...),),),)), ()), None), ('x',)), ())```
>>> dis.dis(x)
1 0 RETURN_VALUE```
a func that only returns
but doesnt gather anything to return
I think this one is fucking the stack
im pretty sure that its actually accessing parts of the current stackframe
(obviously incorrectly)
By the way,
Some advanced styles of programming require updating the types.CodeType object for an existing function. Since code objects are immutable, a new code object needs to be created, one that is modeled on the existing code object. With 19 parameters, this was somewhat tedious. Now, the new replace() method makes it possible to create a clone with a few altered parameters.
This can actually be helpful
(new in 3.8)
can actually be helpful?
it most definitely is.
have you seen my pre-3.8 bytecode patching function which allows for C++-style input and output? it's not pretty :D
!eval py def f(*args, **kwargs): print(args, kwargs) f(**{'13': 13})
@marsh void :white_check_mark: Your eval job has completed with return code 0.
() {'13': 13}
some fun thing I noticed
What even IS esorotic python?
code obfuscation, messing with memory/stack, writing new grammar, golfing stuff
!eval py import ctypes t = (0,) ctypes.c_longlong.from_address(id(t) + 24).value = id(t) print(t, t[0], t[0][0])
@marsh void :white_check_mark: Your eval job has completed with return code 0.
((...),) ((...),) ((...),)
for example :P
ah yes. good ol recursive tuple
custom interpreter
Python 3.8.0 (default, Oct 28 2019, 16:14:01)
[GCC 8.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
(CustomConsole)
>>> f = (x + y from x, y)
>>> f(2, 3)
5
this wasn't as hard as i thought, you can subclass InteractiveConsole from code module and just define your own raw_input, alternatively just create an InteractiveConsole and pass in your own readfunc=
from code import InteractiveConsole
import re
lambda_pattern = re.compile(r'λ.*[.]')
class CustomConsole(InteractiveConsole):
def raw_input(self, prompt=""):
code_ = input(prompt)
matches = re.findall(lambda_pattern, code_)
for match in matches:
code_ = code_.replace(match, f'lambda {", ".join(match[1:-1])}:', 1)
return code_
cc = CustomConsole()
cc.interact()
and we can do:
>>> f = λxy.x + y
>>> f(2, 3)
5
hell yes
Is there any way to compile python code into C code? (I tried Nuitka but that uses C++ I think)
(also I think??? this is the right place to ask this)
you can cythonize it
well you could compile python code into code that includes the entirety of CPython and uses that to evaluate the code
is that cheating?
That's what Cython does, it includes the Python C-API and uses that to do everything requiring that.
is that cheating?
not really
I kinda just need compilable C code from python code
so that could work
how would I do that?
Out of interest, how much Python code do you have?
I don't really have any specific python code
there's a thing that compiles C code to brainfuck, and I'm curious if I go from Python -> C -> Brainfuck
Okay, hang on a sec
wouldnt python -> brainfuck -> c be more interesting?
wdym?
I'm seeing if I can compile python code to Brainfuck
which can only be done by compiling to C first (because ELVM only supports C as a frontend)
[akarys@mojito Projects]$ python
Python 3.8.1 (default, Feb 17 2020, 11:48:46)
[GCC 9.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import glob
>>> total = 0
>>> for f in glob.iglob('./**/*', recursive=True):
... if f.endswith('.py'):
... with open(f) as fh:
... total += len(fh.readlines())
...
>>> total
507852```Hmm, a lot apparently
Why not compiling python bytecode to brainfuck?
how complex is python bytecode?
do you think it'd be possible to compile it to ELVM? (https://github.com/shinh/elvm/blob/master/ELVM.md)
because if that were possible you could compile python to a ton of languages
The issue is that python is stack based
what you recommned for learning about python bytecode?
(I only know the very basic stuff about it)
There is a nice introduction to bytecode at the end of the pytricks book but nothing very advanced
what can be on the stack in python bytecode?
All arguments are passed through the stack
Since python actually use a lot of nested call, that's why it is so important
For instance:
!e
import dis
dis.dis('''
def add(x, y):
return x + y
a = 1
b = 2
add(a, b)''')```
@thin trout :white_check_mark: Your eval job has completed with return code 0.
001 | 2 0 LOAD_CONST 0 (<code object add at 0x7fba2fea85b0, file "<dis>", line 2>)
002 | 2 LOAD_CONST 1 ('add')
003 | 4 MAKE_FUNCTION 0
004 | 6 STORE_NAME 0 (add)
005 |
006 | 5 8 LOAD_CONST 2 (1)
007 | 10 STORE_NAME 1 (a)
008 |
009 | 6 12 LOAD_CONST 3 (2)
010 | 14 STORE_NAME 2 (b)
011 |
... (truncated - too many lines)
Full output: https://paste.pythondiscord.com/etozumehev
You can see in the dissasembly of add() the use of LOAD_FAST which put x and y onto the stack before performing BINARY_ADD, which is going to take the two top objects on the stack, add them and put the result on the stack, then RETURN_VALUE resume the program flow with the returned value being on the top of the stack
Then, POP_TOP is used to discard it, because I didn't assigned it
what instructions would I need to implment somthing simple like print(2 + 5)?
>>> import dis
>>> dis.dis('print(2 + 5)')
1 0 LOAD_NAME 0 (print)
2 LOAD_CONST 2 (7)
4 CALL_FUNCTION 1
6 RETURN_VALUE```
LOAD_NAME, LOAD_CONST, CALL_FUNCTION and RETURN_VALUE, apparently
the base of the language : the stack machine, call stack, memory space, a manual implementation of print, and the opt codes up here ^
It might sound complicated, but it is actually very straitforward
The stack machine is just a stack, nothing else, the call stack is also another stack with two functions to store the next memeory address and jump to another address, and another function to pop the top memory address and jump to it, the memory space is just a dict, and the opt codes can all fit in ~20 lines each
when compiling 5 + 3 it gives
1 0 LOAD_CONST 0 (8)
2 RETURN_VALUE
is there a way to stop it from evaluating beforehand?
I think it is a compiler flag, but I'm not sure
is there a limit to the size either of the stacks can reach?
Apparently you can't disable peephole optimisation
There is one
But it is more limited by the available memory space
I'm kinda trying to limit used for memory to make it easier, so I might just set a fixed max stack size
If you run out of space, you should raise StackOverflow
the limit in CPython is
!e
print(2 ** 32)```
@thin trout :white_check_mark: Your eval job has completed with return code 0.
4294967296
wow
(I expected more)
You can fit 4294967296 32bits memory address in the current implementation
Which is.. 4Gb?
Not sure
137Gb Google did an approximation nevermiiinndd
if there are 4294967296 32-bit addresses then isn't that 16 gigabytes?
how does CALL_FUNCTION know how many positional args there are?
the lowest addressable unit is a single byte
The code obejct knows it
on pretty much all modern CPUs
@gilded orchid the number is the number of arguments
which is why pointer alignment is a thing
1 0 LOAD_NAME 0 (print)
2 LOAD_CONST 2 (7)
4 CALL_FUNCTION 1
6 RETURN_VALUE```
CALL_FUNCTION 1
-> 1 argument
oh right
CALL_FUNCTION just store the next address on the call stack and jump to the beginning of the function
do programs always have RETURN_VALUE at the end?
actually
I just found out you can go from C++ -> C using clang
so it might be possible to go Python -> C++ (using nuitka) -> C (using clang) -> ELVM -> Brainfuck
(even though it'd be horribly inefficient)
Oh man, you'll start with a two line program and finish with a 25 lines program
You're compiling to brainfuck, so I assume efficiency was never a concern.
yeah I guess
25 lines is probably quite conservative.
a C lisp REPL compiled to brainfuck is 26MB
and 52000 lines
(there's some comments added by ELVM)
actually it'd be even more inefficient then I thought it would be
because to convert from C++ -> C you need to convert to LLVM bytecode first
Python -> C++ (using nuitka) -> LLVM Bytecode (using clang) -> C (using llc) -> ELVM Bytecode -> Brainfuck
why not just take the C source of the python command utility, hardcode the source file to some place in memory, and convert that to LLVM
if I may, here's some esoteric C code?
inline int LOG2(uint x){ //x=2^k
static const int tb[32]={31,0,27,1,28,18,23,2,29,21,19,12,24,9,14,3,30,26,17,22,20,11,8,13,25,16,10,7,15,6,5,4};
return tb[x*263572066>>27];
}
someone wanna try figure how that returns 0 to 31
well, it is not guaranteed to work, as it assumes 32bit uint
other than that, any 32bit value rishifted 27 bits will have 5 nonzero bits left, which means at 0-31
the rest are precomputed magic numbers to make it work
@thin trout @gilded orchid https://github.com/python/cpython/pull/13600 almost happened
oh wow
could try out the build from that fork though
@proper vault what is the x*263572066 doing there though
honestly, I doubt it even does log2
I am pretty sure LOG2(3) is not meant to be 18
oh
here's the entire coede
for reference
//g++ 5.4.0
#include <iostream>
#include <cmath>
using namespace::std;
inline int LOG2(uint x){ //x=2^k
static const int tb[32]={31,0,27,1,28,18,23,2,29,21,19,12,24,9,14,3,30,26,17,22,20,11,8,13,25,16,10,7,15,6,5,4};
return tb[x*263572066>>27];
}
int main()
{
for(int i = 0; i < 32; i++){
cout<<LOG2(pow(2,i))<<endl;
}
}
to reproduce results
oh, it is supposed to only work on integer powers of 2
anyway, the reason it is that number specifically, multiplication by a power of 2 is right shift
1111101101011100101001100010
11111
11110
11101
11011
10110
01101
11010
10101
...
```each of these sequences of 5 bits is unique
hm
but I don't see a 00000 in 1111101101011100101001100010 anyplace, how would It get that then
cuz it's like going from 0 to 31
if the number is 2 ** 31 then wouldn't multiplying by that really big number cause it to overflow and have its five highest bits be 00000?
not really suitable for this channel though :D
ye, you would get a large enough number that more 0s would appear, but I seem to be wrong as they are duplicate sequences
well
I'm trying to convert it to python code
tb = [31,0,27,1,28,18,23,2,29,21,19,12,24,9,14,3,30,26,17,22,20,11,8,13,25,16,10,7,15,6,5,4]
for i in range(32):
print(tb[((2**i) * 263572066)>>27])
``` real simple, but I think it isn't right because
it only gives me 0,1,2,3,4 and then gives a list index out of range error
oh, yes, it works with modulo 32!
hm, but also, how did we know it assumes unsigned overflow again?
because shifting a 32 bit value 27 bits will yield at most 31
oh damn, yeah 😅
Wait this conversation gave me an idea
A python scripts that compiles and runs itself
that reminds me, what is the shortest quine in python? ```py
quit(open(file).read())
exec(s:='print("exec(s:=%r)"%s)')
But i guess that's cheating too
Maybe it isn't
I don't know the rules really
Doing more investigation it isn't cheating
and i also found this quine in ruby on which you can remove 1 single character and it will still work
eval='eval$q=%q(puts %q(10210/#{1 1 if 1==21}}/.i rescue##/
1 1"[13,213].max_by{|s|s.size}#"##").gsub(/\d/){["=\47eval$q=%q(#$q)#\47##\47
",:eval,:instance_,"||=9"][eval$&]}
exit)#'##'
instance_eval='eval$q=%q(puts %q(10210/#{1 1 if 1==21}}/.i rescue##/
1 1"[13,213].max_by{|s|s.size}#"##").gsub(/\d/){["=\47eval$q=%q(#$q)#\47##\47
",:eval,:instance_,"||=9"][eval$&]}
exit)#'##'
/#{eval eval if eval==instance_eval}}/.i rescue##/
eval eval"[eval||=9,instance_eval||=9].max_by{|s|s.size}#"##"
And there's another one which is technically legal
print(__name__)
and you have to name the file: print(__name__)
related to the magic log2 function, see also https://en.wikipedia.org/wiki/Fast_inverse_square_root
for i, v in globals().items():
print(f"{i} is {v}")
Make this not raise an exception
without adding lines
And without using copy()
globals = lambda g={**globals()}:g``` add this above it
Golfed: globals=lambda:{}
well i assumed he wanted the same output
The only output of their program is __name__ is __main__ for me.
Can I remove lines?
||```py
print(*[f"{i} is {v}" for i, v in globals().items()], sep='\n')
Python 3.8.2 (default, Feb 26 2020, 22:21:03)
[GCC 9.2.1 20200130] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> for i, v in globals().items():
... print(f"{i} is {v}")
...
__name__ is __main__
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
RuntimeError: dictionary changed size during iteration
>>>
I can give my neofetch, but I don't think it'll be useful.
oh itll print more if you get around the exception
I think I have found a trick that will confuse some people.
!e
# ,= is a brand new operator in Python, and a very useful one.
# It's like += or :=, but with a comma.
# It unpacks a single-element iterable
# into its only element. It's not documented yet!
my_list = ["hello"]
x ,= my_list
print(x)
my_generator = (x for x in [1, 2, 3] if x < 2)
number ,= my_generator
print(number)
@formal sandal :white_check_mark: Your eval job has completed with return code 0.
001 | hello
002 | 1
haha, that's fun
nicer alternative 😛
>>> my_generator = (x for x in [1, 2, 3] if x < 2)
>>> [number] = my_generator
>>> number
1
Yes, but it doesn't look like := or += or ->=
What does unpacking accept? Just tuples and lists?
Any iterable.
on the left side
@formal sandal :white_check_mark: Your eval job has completed with return code 0.
1
technically that's not a Python tuple
did you mean to print(a)?
Seems to be only parentheses and brackets
assignment_stmt ::= (target_list "=")+ (starred_expression | yield_expression)
target_list ::= target ("," target)* [","]
target ::= identifier
| "(" [target_list] ")"
| "[" [target_list] "]"
| attributeref
| subscription
| slicing
| "*" target
:D
that pinged Static for some reason, not staticmethod
I think I reinvented pug... But maybe this can be useful.
Input:
%extends[base.html]
%block(body)[
header(1)[ Lorem ipsum ]
p[
Dolor sit amet!
]
p[
[Welcome to] fontsize(2em)[my] [homepage!]
]
]
Output:
{% extends "base.html" %}
{% block body %}
<h1> Lorem ipsum </h1>
<p> Dolor sit amet! </p>
<p>Welcome to
<span style='font-size: 2em'>my</span>
homepage!</p>
{% endblock %}
Because I'm so tired of writing a tag twice!
def test(a):
b,=a
return a==b
challenge using that: make this return true
!e
def test(a):
b,=a
return a==b
x = []
x.append(x)
print(test(x))
@formal sandal :white_check_mark: Your eval job has completed with return code 0.
True
!e
def test(a):
b,=a
return a==b
print(test("a"))
def test(a):
b,=a
return a==b
x=[]
x.append(x)
print(test(x))
@formal sandal :white_check_mark: Your eval job has completed with return code 0.
True
!e
import inspect
import ast
def namespace(fn):
source = inspect.getsource(fn)
tree = ast.parse(source)
local_names_before = set(locals().keys())
tree.body = tree.body[0].body
"""
before: after:
def fn(): a = 1
a = 1 b = 2
b = 2
"""
exec(compile(tree, "<namespace function>", "exec"))
new_locals_dict = locals()
local_names_after = set(new_locals_dict.keys()) - {"local_names_before"}
new_keys = local_names_after - local_names_before
return {key: new_locals_dict[key] for key in new_keys}
@namespace
def MyVariables():
a = 1
b = 2
def c():
return "letter c"
print(MyVariables)
@formal sandal :x: Your eval job has completed with return code 1.
001 | Traceback (most recent call last):
002 | File "<string>", line 22, in <module>
003 | File "<string>", line 5, in namespace
004 | File "/usr/local/lib/python3.8/inspect.py", line 985, in getsource
005 | lines, lnum = getsourcelines(object)
006 | File "/usr/local/lib/python3.8/inspect.py", line 967, in getsourcelines
007 | lines, lnum = findsource(object)
008 | File "/usr/local/lib/python3.8/inspect.py", line 798, in findsource
009 | raise OSError('could not get source code')
010 | OSError: could not get source code
Anyway, here's the toy markup parser.
main: https://hastebin.com/upazakovax.py
grammar: https://hastebin.com/ukonasarin.sql
util: https://hastebin.com/coyemodoxe.py
It's just the beginning, but it's easy to extend.
After a bit of tinkering, I was able to make it work!
%extends[base.html]
%block(body)[
header(1)[ Lorem ipsum ]
p[
Dolor sit amet!
]
p[
[Welcome to] fontsize(2em)[my] [homepage!]
]
todo [make everything work]
.class-a class-b class-c [
p[hello]
]
#my_id [
p[world]
]
style[
td {border: 1px solid black; padding: 1em}
][
table(2, 3)
[a] [b] [c]
[d] [e] [f]
elbat
]
]
This is how it works on the inside:
is it like html but tags are a[stuff] instead of <a>stuff</a>?
Well, yes.
And making tables is not that bad anymore.
Also, as you can see, you can use normal CSS (even SCSS!) in inline styles.
Kinda like pug https://pugjs.org/api/getting-started.html.
But pug uses semantic indents instead of [].
seems pretty similar to latex syntax-wise
now it's time to implement tikz...
I would also love table styling similar to latex's arrays, where I can configure alignment, put vertical bars etc.
Here's the updated interactive version
https://repl.it/@int6h/EmuParser
@formal sandal what are you doing if I may ask ?
@formal sandal do you know of the dominate and pylatex packages ? It kinda looks like you’re merging the two into yet another parser/lexer/doodat
it parses emu-speak
@formal sandal if you’re going to integrate tils there is a bit of a hack you can try. Putting ellipsis as the first argument in a python function makes the argspec extra flexible so
def path(..., *args, **kvps) :
#stuff
Gave you an extra argument span to play with and allows more Tex/TikZ like syntax. I forget the full trick but I think you could add key word args before the ellipsis iirc. I only tried it in python 3.6 and a self compiled 3.5 which was rather dodgey.
Never heard of that I’ll google it up.
@grizzled cloak ok my google foo broke, what is emu speak ?
My first google hit was “emu’s are gods” so I was like that ain’t parseltongue 😉
@grizzled cloak
@opal perch I'm just making a convenient tool for myself. I'm not trying to replicate latex or something.
I will take a look at dominate one day, it looks cool
Like elm where HTML tags are just functions.
@formal sandal Ok, hmm... do know of the pyparsing/pylens libraries ? Might help with the parsing. Pyparsing allows you to convert the text as you parse it if you use it right. So you could do wierd things like split your emu into css and html. Which is the best way to eat em really.
FYI I met an Australian chef once. Told me the best way to cook emu was with two pots. In the first you place the emu and in the second a brick. When the brick is soft enough to prick with a fork you know that the emu is ready.
I'm already using Lark which uses EBNF-like grammar, and I'm quite happy with it.
It has a billion extra features I don't even know about...
Ok, the pylens one was interesting as it led to a project called augeas which allowed one to edit /etc files programmatically.
How have you found lark ? I haven’t tried it personally.
Granted when I last tried I couldn’t find a nice intro.
Lark is very convenient and declarative. You just define the grammar and some handlers inside a transformer.
There are some tutorials and lots of examples at the repo: https://github.com/lark-parser/lark
It takes a grammar, a textual input and produces an abstract syntax tree.
Then you can either use a Visitor or a Transformer.
I have never used a Visitor, though.
Ok, can you transform the AST and flatten it again to the original text ?
Is it possible to include a custom class as the basis for the AST nodes ? I tried something similar in pyparsing with QObjrct to display the AST in a gui. But there are other applications.
How do the transformers works. Also thanks for the link I’ll plough through em tomorrow 🙂
Well, you can create a transformer that will eventually returns the string that's like the original one.
Basically, you can
Can you access the ast through any other patterns ? E.g. can you travers the ast yourself or does lark control that.
You can.
For an example, you can look at my other project:
https://repl.it/@int6h/GlorifiedLambdaCalculus
in grammar.lark and _parser.py
Cool, I’ll do so. Thanks !
!e
class Context:
def __enter__(self):
self.__backup = {**__annotations__}
__annotations__.clear()
return self
def __exit__(self, *s):
for key, val in __annotations__.items():
setattr(self,key,val)
__annotations__.clear()
__annotations__.update(self.__backup)
del self.__backup
with Context() as c:
x: 123
y: 789
print(c.x, c.y)
@rugged sparrow :white_check_mark: Your eval job has completed with return code 0.
123 789
!e
# ,= is a brand new operator in Python, and a very useful one. # It's like += or :=, but with a comma. # It unpacks a single-element iterable # into its only element. It's not documented yet! my_list = ["hello"] x ,= my_list print(x) my_generator = (x for x in [1, 2, 3] if x < 2) number ,= my_generator print(number)
hey v_gz, how did you know about it if it hasn't been documented yet
lol
cute
that's just abuse of syntax
x, = my_list
which is more apparent if i do this
@formal sandal have you looked at beazley's parser: https://sly.readthedocs.io/en/latest/
No I haven't yet.
SLY would work as well, I guess.
How do language parsers work with context-dependent grammar?
Like in C++:
Template1 <Template2<T>> a;
((Template1 < Temlpate2) < T) >> a
or
declare a of type Template1[type=Template2[type=T]]?
well sly allows lets you declare precedences to avoid reduce reduce conflicts
That's possible in Lark as well.
i did a parser for boolean logic with sly just to get familiar with it
Just to say, if your grammar is simple enough and LL(1) you can generate a parser without depending any library. At least until 3.10.
Thanks to lib2to3.pgen2
Maybe.
There are probably too many options for parsing in python...
Anyway, here's what I added.
from ..ext import EmuExtension, register_extension
from ..util import namespace, splat
@register_extension
class BasicTable(EmuExtension):
grammar_entry_point = "FUNCTION_TAGS"
name = "tagf_table"
rule = ' "table" "(" POSITIVE_INTEGER "," POSITIVE_INTEGER ")" ("[" atom "]")+ "elbat" '
@namespace(fn=splat)
def handlers():
def tagf_table(width, height, *cells):
if len(cells) != width * height:
return f"[BasicTable: expected {width * height} cells, got {len(cells)}]"
output = "<table>"
for j in range(height):
output += "<tr>"
for i in range(width):
output += "<td>" + cells[width * j + i] + "</td>"
output += "</tr>"
output += "</table>"
return output
Now you can easily add your own extensions to the parser.
table (3, 2)
[a] [b] [c]
[d] [e] [f]
elbat
i can't find any docs for pgen
Well, there are docs inside the code.
where
Oh, hm
The actuan pgen doesn't have any docs, I guess
# Copyright 2004-2005 Elemental Security, Inc. All Rights Reserved.
# Licensed to PSF under a Contributor Agreement.
I'm not sure whether they can add docs to the code
Oh wait
@zealous widget
https://github.com/python/cpython/tree/a86b522d8f1c8b9c26b5550de221d2227158cf4d/Parser/pgen
!e
my_list = ["hello"]
x ,= my_list
print(x)
my_generator = (x for x in [1, 2, 3] if x < 2)
number ,= my_generator
print(number)
@wide depot :white_check_mark: Your eval job has completed with return code 0.
001 | hello
002 | 1
what were you doing?
Testing unpacking the generators?
the ,= operator
hey v_gz, how did you know about it if it hasn't been documented yet
We have connections to the CPython core dev team in the staff channel
Now this is actually useful.
[
p[ Hello world. ]
p[ [X] ?in [set of all X's] ]
p[
py-exec
[print("Hello, world!")]
[Hello, world!]
]
p[
py-exec
[print("Hello, world!")]
[Hello, world...]
]
]
@formal sandal any source?
?
Yes.
Wanted to know if it is open-source
I'm going to publish it eventually, of course.
I can share it on github if you really want to see it, but it really needs refactoring in some places.
Yeah I don’t mind messy code
I am interested in how it gets translated and stuff basically
Oh, I'm just using lark, as I said before. So there's nothing really interesting in terms of parsing. But maybe I'm wrong.
Cool! :)
RE: pgen, I ran across this https://python-history.blogspot.com/2018/05/the-origins-of-pgen.html
David Beazley's talk at US PyCon 2018, about parser generators, reminded me I should write a bit about its history. Here's a brief brain dum...
Hello
how can i override a _dict_ of an Enum?
Code:
class Color(Enum):
pass
globals().get("Color").__dict__.update(colorama.Fore.__dict__)
Also tried calling Color() but it doesn't work
nvm
What's your goal exactly?
you could just do ```py
from colorama import Fore as Color
alternatively,
from collections import SimpleNamespace
from colorama import Fore
Color = SimpleNamespace(**vars(Fore))
```or
```py
from colorama import Fore
Color = type('Color', (), vars(Fore))
sum(iter(int,1))
terrible way of creating an infinite loop
and a terrible way of hitting the recursion limit
a=iter(a);next(a)
Doesn't that give a nameerror?
nope
!e
a=iter(a);next(a)
@edgy kelp :x: Your eval job has completed with return code 1.
001 | Traceback (most recent call last):
002 | File "<string>", line 1, in <module>
003 | NameError: name 'a' is not defined
!e ```py
a = []
a = iter(a + [a])
next(a)
@distant wave :warning: Your eval job has completed with return code 0.
[No output]
!e ```py
a = (a := []) + [a]
print(a)
@distant wave :white_check_mark: Your eval job has completed with return code 0.
[[]]
@distant wave :white_check_mark: Your eval job has completed with return code 0.
[[...]]
a = (a := []) + [a]
# is the same as
a = []
a = a + [a]```
!e ```py
b = (a := [1, 2, 3])
a.append(4)
print(a, b)
@distant wave :white_check_mark: Your eval job has completed with return code 0.
[1, 2, 3, 4] [1, 2, 3, 4]
!e ```py
b = (a := [1, 2, 3]) + []
a.append(4)
print(a, b)
@distant wave :white_check_mark: Your eval job has completed with return code 0.
[1, 2, 3, 4] [1, 2, 3]
assigning to a variable doesn't mutate the value in the variable, it just replaces it entirely with something else
yep
+= calls __iadd__ or whatever it is
which for lists changes it in-place
what i said was about this
that makes perfect sense and i don't really understand what's confusing
There's only one list initializer called, yet there's two lists in the output
!e ```py
a = (a := [1]) + [a]
print(a)
@distant wave :white_check_mark: Your eval job has completed with return code 0.
[1, [1]]
Is := really a good addition to python?
Are there any uses of it besides while loops?
Oh, true
I'm not a fan of all the parentheses you sometimes need but it can make simple conditions nicer
You can also create a new variable like this inside a list comprehension, I guess
Yeah also nice there to reduce the operations but that leaks vars, there's also this for the list comps https://docs.python.org/3.9/whatsnew/3.9.html#optimizations
not nearly as readable though
The walrus is just syntax sugar anyway, it will probably never allow something we couldn't do before
Except if you try to do golfing or functional programming
The walrus is just syntax sugar anyway, it will probably never allow something we couldn't do before
It was, at least kind of, but not now.
!e
(a:=[]).append(a)
@rugged sparrow :warning: Your eval job has completed with return code 0.
[No output]
!e
(a:=[]).append(a)
print(a)
@rugged sparrow :white_check_mark: Your eval job has completed with return code 0.
[[...]]
!e
(a:=[1,]).append(a)
for _ in range(10):
a = a[0]
print(b)
@sick hound :x: Your eval job has completed with return code 1.
001 | Traceback (most recent call last):
002 | File "<string>", line 4, in <module>
003 | NameError: name 'b' is not defined
!e
(a:=[1,]).append(a)
for _ in range(10):
a = a[0]
print(a)
@sick hound :x: Your eval job has completed with return code 1.
001 | 1
002 | Traceback (most recent call last):
003 | File "<string>", line 3, in <module>
004 | TypeError: 'int' object is not subscriptable
!e
(a:=[1,]).append(a)
print(a)
@sick hound :white_check_mark: Your eval job has completed with return code 0.
[1, [...]]
!e
(a:=[1,]).append(a)
for _ in range(10):
a = a[1]
print(b)
@sick hound :x: Your eval job has completed with return code 1.
001 | Traceback (most recent call last):
002 | File "<string>", line 4, in <module>
003 | NameError: name 'b' is not defined
!e
(a:=[1,]).append(a)
for _ in range(10):
a = a[1]
print(a)
@sick hound :white_check_mark: Your eval job has completed with return code 0.
001 | [1, [...]]
002 | [1, [...]]
003 | [1, [...]]
004 | [1, [...]]
005 | [1, [...]]
006 | [1, [...]]
007 | [1, [...]]
008 | [1, [...]]
009 | [1, [...]]
010 | [1, [...]]
infinitely nested
nicer
nice
useful
!e
(a:=[]).append(a)
(b:=[]).append(b)
print(a == b)
@sick hound :x: Your eval job has completed with return code 1.
001 | Traceback (most recent call last):
002 | File "<string>", line 3, in <module>
003 | RecursionError: maximum recursion depth exceeded in comparison
I think this is possible to make this return True though
!e py (a := []).append(a) print(a == a)
@marsh void :white_check_mark: Your eval job has completed with return code 0.
True
!e
def f(x):
try: f(f(f(f(0))))
except: f(f(f(f(0))))
f(0)
@formal sandal :x: Your eval job has completed with return code 139 (SIGSEGV).
001 | Fatal Python error: Cannot recover from stack overflow.
002 | Python runtime state: initialized
003 |
004 | Current thread 0x00007f5636b4e740 (most recent call first):
005 | File "<string>", line 2 in f
006 | File "<string>", line 2 in f
007 | File "<string>", line 2 in f
008 | File "<string>", line 2 in f
009 | File "<string>", line 2 in f
010 | File "<string>", line 2 in f
011 | File "<string>", line 2 in f
... (truncated - too many lines)
Full output: too long to upload
!e print(0)
@formal sandal :white_check_mark: Your eval job has completed with return code 0.
0

!e ```py
def f():
try:f()
except:f()
f()
@rugged sparrow :x: Your eval job has completed with return code 139 (SIGSEGV).
001 | Fatal Python error: Cannot recover from stack overflow.
002 | Python runtime state: initialized
003 |
004 | Current thread 0x00007f4435a18740 (most recent call first):
005 | File "<string>", line 2 in f
006 | File "<string>", line 2 in f
007 | File "<string>", line 2 in f
008 | File "<string>", line 2 in f
009 | File "<string>", line 2 in f
010 | File "<string>", line 2 in f
011 | File "<string>", line 2 in f
... (truncated - too many lines)
Full output: too long to upload
i think doing try/except resets the way it keeps track of recursion that triggers the recursion limit
so if you do that you get an actual stack overflow
also
nah its cause the generic try/except catches the Recursion exception
!e
f=int
f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f())))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))
@formal sandal :x: Your eval job has completed with return code 1.
001 | s_push: parser stack overflow
002 | MemoryError
^
!e ```py
def f():
try:f()
except Exception as e:print(e)
f()
@rugged sparrow :white_check_mark: Your eval job has completed with return code 0.
maximum recursion depth exceeded
!e ```py
def f():
try:f()
except Exception as e:
print(e)
f()
f()
@snow beacon :x: Your eval job has completed with return code 139 (SIGSEGV).
001 | maximum recursion depth exceeded
002 | Fatal Python error: Cannot recover from stack overflow.
003 | Python runtime state: initialized
004 |
005 | Current thread 0x00007f0e874ee740 (most recent call first):
006 | File "<string>", line 2 in f
007 | File "<string>", line 2 in f
008 | File "<string>", line 2 in f
009 | File "<string>", line 2 in f
010 | File "<string>", line 2 in f
011 | File "<string>", line 2 in f
... (truncated - too many lines)
Full output: too long to upload
Okay, I don't know why I expected anything different.
Maybe it would be possible to open multiple processes or threads running this function and see what it does.
Like, 256 processes.
Not sure multiple processes work in snekbox.
!e
from multiprocessing import Pool
def f(x):
return x*x
with Pool(5) as p:
print(p.map(f, [1, 2, 3]))
@formal sandal :x: Your eval job has completed with return code 1.
001 | Traceback (most recent call last):
002 | File "<string>", line 6, in <module>
003 | File "/usr/local/lib/python3.8/multiprocessing/context.py", line 119, in Pool
004 | return Pool(processes, initializer, initargs, maxtasksperchild,
005 | File "/usr/local/lib/python3.8/multiprocessing/pool.py", line 191, in __init__
006 | self._setup_queues()
007 | File "/usr/local/lib/python3.8/multiprocessing/pool.py", line 343, in _setup_queues
008 | self._inqueue = self._ctx.SimpleQueue()
009 | File "/usr/local/lib/python3.8/multiprocessing/context.py", line 113, in SimpleQueue
010 | return SimpleQueue(ctx=self.get_context())
011 | File "/usr/local/lib/python3.8/multiprocessing/queues.py", line 336, in __init__
... (truncated - too many lines)
Full output: too long to upload
People say that there are no macros in Python.
But we have inspect!
!e
import inspect
def destructure(obj):
if not isinstance(obj, dict):
raise TypeError(f"{obj} is not a dict")
parent_frame = inspect.currentframe().f_back
line = inspect.getframeinfo(parent_frame).code_context[0]
var_names = map(str.strip, line.split("=")[0].split(","))
for var_name in var_names:
yield obj[var_name]
my_dictionary = {"a": 42, "varr": 666, "_": ()}
a, varr, _ = destructure(my_dictionary)
print(a)
print(varr)
print(_)
@formal sandal :x: Your eval job has completed with return code 1.
001 | Traceback (most recent call last):
002 | File "<string>", line 13, in <module>
003 | File "<string>", line 7, in destructure
004 | TypeError: 'NoneType' object is not subscriptable
:___ - (
Apparently code_context is None even for the frames that were mine!
In a normal environment, it would print this:
exhausting an iterable?
Not quite, I suppose I wasn't very clear, this'll be used on it's own line, when interspersed with other, normally named code
is the whole line consists from that single expression?
yes
then, I couldn't think anything else then just getting all values from an iterable/generator
Want a tiny hint?
yup
When is _ a defined variable?
in shell
when you use that translation module
!e
from multiprocessing import Pool
def f(x):
return x*x
with Pool(5) as p:
print(p.map(f, [1,m]))
@sick hound :x: Your eval job has completed with return code 1.
001 | Traceback (most recent call last):
002 | File "<string>", line 6, in <module>
003 | File "/usr/local/lib/python3.8/multiprocessing/context.py", line 119, in Pool
004 | return Pool(processes, initializer, initargs, maxtasksperchild,
005 | File "/usr/local/lib/python3.8/multiprocessing/pool.py", line 191, in __init__
006 | self._setup_queues()
007 | File "/usr/local/lib/python3.8/multiprocessing/pool.py", line 343, in _setup_queues
008 | self._inqueue = self._ctx.SimpleQueue()
009 | File "/usr/local/lib/python3.8/multiprocessing/context.py", line 113, in SimpleQueue
010 | return SimpleQueue(ctx=self.get_context())
011 | File "/usr/local/lib/python3.8/multiprocessing/queues.py", line 336, in __init__
... (truncated - too many lines)
Full output: too long to upload
@sick hound :warning: Your eval job has completed with return code 0.
[No output]
!e
from multiprocessing import Pool
with Pool:
paas
@sick hound :x: Your eval job has completed with return code 1.
001 | Traceback (most recent call last):
002 | File "<string>", line 3, in <module>
003 | AttributeError: __enter__
!e
from multiprocessing import Pool
with Pool:
pass
@sick hound :x: Your eval job has completed with return code 1.
001 | Traceback (most recent call last):
002 | File "<string>", line 3, in <module>
003 | AttributeError: __enter__
!e
from multiprocessing import Pool
with Pool(1):
pass
@sick hound :x: Your eval job has completed with return code 1.
001 | Traceback (most recent call last):
002 | File "<string>", line 3, in <module>
003 | File "/usr/local/lib/python3.8/multiprocessing/context.py", line 119, in Pool
004 | return Pool(processes, initializer, initargs, maxtasksperchild,
005 | File "/usr/local/lib/python3.8/multiprocessing/pool.py", line 191, in __init__
006 | self._setup_queues()
007 | File "/usr/local/lib/python3.8/multiprocessing/pool.py", line 343, in _setup_queues
008 | self._inqueue = self._ctx.SimpleQueue()
009 | File "/usr/local/lib/python3.8/multiprocessing/context.py", line 113, in SimpleQueue
010 | return SimpleQueue(ctx=self.get_context())
011 | File "/usr/local/lib/python3.8/multiprocessing/queues.py", line 336, in __init__
... (truncated - too many lines)
Full output: too long to upload
Was it worth spamming here?
@distant wave in the interactive shell, when the previous result was an Iterable (itertools/map) and you wish to inspect the result
^ that's it
You can golf it a little by doing *_, instead.
that's what isidentical said
neat
IMO exhausting an iterable and unpacking it to view its contents are not the same
how do you unpack it without exhausting it
You don't
!e ```py
exhausting an iterable:
[*range(15)]
showing an iterables contents
print([*range(15)])
@distant wave :white_check_mark: Your eval job has completed with return code 0.
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
You are still exhausting an iterable, but the purposes are different
i don't think a purpose was mentioned
these two functions are different ```py
def function_one():
2 + 2
def function_two():
return 2 + 2```
they do the same thing but you get different results
yes, but this was a console-only question
isidentical: then, I couldn't think anything else then just getting all values from an iterable/generator
i'm still on team isidentical here
i think maybe you were overly pedantic
i can try out custom console functions with this though:
In [20]: %a 1 2 3 4
Out[20]: array([1, 2, 3, 4])
In [21]: map(λx.2x, _)
Out[21]: <map at 0x7f245b2fcac0>
In [22]: *_,
Out[22]: (2, 4, 6, 8)
Got an idea from that discussion
import sys
import itertools
import builtins
def displayhook(value):
if value is None:
return
print(repr(value))
try:
iter(value)
a, b = itertools.tee(value)
print("Saved you unpacking:", list(a))
builtins._ = b
except TypeError:
pass
sys.displayhook = displayhook
Messes up the original variable but I guess it's handy if you can't be bothered typing 3 characters 🤷♂️
that might be a fun one to add to the console
then again, i'd probably kill it with an infinite iterator
maybe put a limit on there
lol true
you can subclass InteractiveConsole too
from code import InteractiveConsole
import re
lambda_pattern = re.compile(r'λ[a-z]+[.]')
class CustomConsole(InteractiveConsole):
def raw_input(self, prompt=""):
code_ = input(prompt)
matches = re.findall(lambda_pattern, code_)
for match in matches:
code_ = code_.replace(match, f'lambda {", ".join(match[1:-1])}:', 1)
return code_
cc = CustomConsole()
cc.interact()
just mess with the raw_input function to do whatever wait, you'd still need a hook for output
well, there's probably a thing for that
no, i think you still need sys
rip
something like this maybe
import sys
import itertools
import builtins
from itertools import tee, islice
def isgenerator(iterable):
return hasattr(iterable,'__iter__') and not hasattr(iterable,'__len__')
def displayhook(value):
if not isgenerator(value):
print(repr(value))
return
a, b = itertools.tee(value)
items = *islice(a, 11),
print(*items[:10], '...' if len(items) == 11 else '')
builtins._ = b
sys.displayhook = displayhook
ah nice I spent a while thinking about how to detect if something's a generator
does anyone know of another way to do this -- automatically add a parent's init to a child's init while also combining the signatures:
from inspect import signature
from textwrap import dedent
def parse_signature(signature):
return str(signature)[1:-1].split(', ', 1)[1]
class Person:
def __init__(self, name):
self.name = name
def __init_subclass__(cls):
def deco(func):
child_args = parse_signature(signature(func))
self_args = parse_signature(signature(cls.__base__.__init__))
all_args = f'{self_args}, {child_args}'
temp_globals = {'func': func, 'cls':cls}
temp_locals = {}
wrapper = f"""
def wrapper(self, {all_args}):
super(cls, self).__init__({self_args})
return func(self, {child_args})"""
exec(dedent(wrapper), temp_globals, temp_locals)
return temp_locals['wrapper']
cls.__init__ = deco(cls.__init__)
class Cyborg(Person):
def __init__(self, version):
self.version = version
it's mostly so this little pop up is still informative
note how it gives the parent's __init__ variable as well
i'm more curious if there's a way to do this without exec, or even without inspect
it's great that i don't have to call super() anymore though, works for children of children too!:
technically it would probably be possible to do it without needing exec or inspect if you just did a lot of stuff yourself with code objects
except they're immutable
yeah i wasn't saying you would change the code objects
just look at the code objects and then make a new one
I kind of did that, but not exactly as your case. Maybe helps @zealous widget https://github.com/isidentical/asteria/blob/master/asteria/asteria.py#L129-L142
Your reminder will arrive in 12 hours!
`chain = Chain().print(4).print(5).iterate(x = [1, 2, 3]).print(v('x')).end().print(5)
chain.top.execute()`
another method for anonymous functions if you want
it works by using .end()
working on improving it, but it works so far
i might add __call__ to it
so you could do chain() over chain.top.execute()
yep, I added __call__
@zealous widget
Here's your reminder: https://github.com/isidentical/asteria/blob/master/asteria/asteria.py#L129-L142.
Jump back to when you created the reminder
ok. i am very confused
shouldnt __slots__ decrease memory usage?
class A(object):
__slots__ = ("foo", "bar", "baz")
def __init__(self, foo, bar, baz):
self.foo = foo
self.bar = bar
self.baz = baz
def __repr__(self):
return str(sys.getsizeof(self))
class B(object):
def __init__(self, foo, bar, baz):
self.foo = foo
self.bar = bar
self.baz = baz
def __repr__(self):
return str(sys.getsizeof(self))
A(1,2,3)
Out[7]: 64
B(1,2,3)
Out[8]: 56```
why is the non slots version smaller???
I wouldn't rely on getsizeof too much
getsizeof just gets the size of the actual object, not the amount of memory usage
maybe the non-slots version has a reference to some other big thing
Try getting sizes of dicts etc. It only gets the upper structures and I wouldn't use it for custom object without implementing the finder.
One way you could try it is making a whole lot of objects
trying that now ^^
Should also be faster to create them
Index Count % Size % Cumulative % Kind (class / dict of class)
1 1000001 23 64_000_064 34 148_278_904 79 __main__.A
2 1000001 19 56_000_056 19 252_279_548 86 __main__.B```
A is with slots, b without
bit more than half the size
inspected with guppy.hpy().heap()
Oh, sorry for the ping then.
Anyway, if you'll want to try it out, it comes with an adapter to the pyramid web framework
nice
https://github.com/gvanrossum/ctok
CPython's tokenizer exposed as a Python class
huh?
saved you some code for tokenizing a string
tokenize is written in python to behave like the original tokenizer
it is not using same approach with the original tokenizer
(it is using regexps, tons of regexps)
!e ```py
def my_function():
print("hi!")
exec(f"my_function{'.call' * 2991}()")
@midnight bane :white_check_mark: Your eval job has completed with return code 0.
hi!
!e ;
!e ```py
def my_function():
print("hi!")
exec(f"my_function{'.call' * 2993}()")
@marsh void :x: Your eval job has completed with return code 1.
001 | Traceback (most recent call last):
002 | File "<string>", line 4, in <module>
003 | RecursionError: maximum recursion depth exceeded during compilation
yup
Just had someone ask me why their code doesnt work:
import pyglet
from pyglet.window import key
from pyglet.gl import glClearColor
#window stuff
window = pyglet.window.Window(800, 600, caption = 'game', fullscreen = False)
glClearColor(0,128 / 255,0 ,1)
#test
keys = key.KeyStateHandler()
if keys[key.SPACE] == True:
print('hello')
#player stuff
class player():
global player_image
player_image = pyglet.resource.image('player1.png')
global player_X
player_X = 350
global player_Y
player_Y = 250
#update
def update(dt):
window.push_handlers(keys)
pyglet.clock.schedule_interval(update, 1/120)
#i dont know stuff
@window.event
def on_draw():
window.clear()
player_image.blit(player_X,player_Y)
#to make this work
pyglet.app.run()
You can __call__ the __call__ hmm
import pyglet
from pyglet.window import key
from pyglet.gl import glClearColor
window = pyglet.window.Window(800, 600, caption='game', fullscreen=False)
glClearColor(0, 128 / 255, 0, 1)
keys = key.KeyStateHandler()
class player():
image = pyglet.resource.image('player1.png')
x = 0
y = 0
@window.event
def on_draw():
window.clear()
player.image.blit(player.x, player.y)
def update(dt):
window.push_handlers(keys)
if keys[key.ENTER]:
print("Works")
pyglet.clock.schedule_interval(update, 1/120)
pyglet.app.run()
``` Fixed
How is it related to this channel
!e python import sys print(sys.executable) print('Hello World!')
@pure junco :white_check_mark: Your eval job has completed with return code 0.
001 | /usr/local/bin/python
002 | Hello World!
!e python import requests r = requests.get('http://api.ipify.org') print(r.text) r.close()
@pure junco :x: Your 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 'requests'
there are #bot-commands if you want to try !e
Yeah, its really weird how you can call the call
I guess it makes sense though because __call__ is callable so you can call it
what is code golfing 
getting a source that does something in as few characters as possible
really happy I'm already getting it down to this, but trying to reduce it even further. Any tips?
get_p = lambda e : g[e[0]][e[1]].get('passengers') #get the passenger load from a link
sumload = lambda n: sum([get_p(edge) for edge in g.edges() if n in edge]) #sum all passengers loads if a node is part of the link
df['aggregated_passengers'] = pd.Series([(node, sumload(node)) for node in G.nodes()]) #create series of the link using list comprehension
df['aggregated_passengers'] = pd.Series([(node, (lambda n: sum([(lambda e: g[e[0]][e[1]].get('passengers'))(edge) for edge in g.edges() if n in edge]))(node)) for node in G.nodes()])
```compressed to a single line^
df['aggregated_passengers'] = pd.Series((n, sum(g[e[0]][e[1]].get('passengers') for e in g.edges() if n in e)) for n in G.nodes())
not sure why have that many lambdas
i think the idea of what lumen did was just replacing names with the thing they're defined as
e.g. ```py
add_one = lambda x: x + 1
number = add_one(4)
becomes
number = (lambda x: x + 1)(4)```
then lakmatiol noticed that having all those lambdas and then immediately calling them is kind of redundant ```py
number = (lambda x: x + 1)(4)
becomes
number = 4 + 1```
if you want to make it even shorter then you can remove some spaces py df['aggregated_passengers']=pd.Series((n,sum(g[e[0]][e[1]].get('passengers')for e in g.edges()if n in e))for n in G.nodes())
huh
huh
SASS is just too cool to be true.
@mixin outline-divs($depth)
@if $depth > 0
> div
border: 1px solid black
@include outline-divs($depth - 1)
body
@include outline-divs(5)
compiles to:
body > div {
border: 1px solid black;
}
body > div > div {
border: 1px solid black;
}
body > div > div > div {
border: 1px solid black;
}
body > div > div > div > div {
border: 1px solid black;
}
body > div > div > div > div > div {
border: 1px solid black;
}
gcd = lambda a,b : a if not b else gcd(b, a%b)
Any way you can golf that further? (Code for gcd of 2 numbers)
For a start you can remove spaces around the punctuation.
yeah, apart from that
I included the spaces here just so that it's easier on people's eyes here
chain = (
Chain('a').
iter(x = [1, 2]).
iter(y = [1, 2]).
print(v.a, '---', v.x, v.y).
end().
end()
)
chain(5)```
i plan to add conditions
chain = (
Chain('x').
condition(v = True). #equality: v == True
condition(lambda v: v.x > 3).
print(v.x).
end().
end()
)```
but for a python equalivent.
#CHAINS
chain = (
Chain('a').
iter(x = [1, 2]).
iter(y = [1, 2]).
print(v.a, '---', v.x, v.y).
end().
end()
)
chain(5)
#DEF
def chain(a):
for x in [1, 2]:
for y in [1, 2]:
print(a, '---', x, y)
chain(5)```
lambda a,b : a if not b else gcd(b, a%b)
Any way you can golf that further? (Code for gcd of 2 numbers)
Would[gcd(b,a%b),a][b!=0]work? Since a and b are numbers
not b is 1 longer than b!=0
true
b==0?
conditions reversed, same length
f=lambda a,b:a if b<1else f(b,a%b)
^ Fails for negative b
i think that's as good as you can go
unless you do
[gcd(b, a % b), a][b!=0]
hold on
for my chains thing
you can use them easily in functions
print(
Chain('a').
iter(x = [1, 2]).
iter(y = [1, 2]).
print(v.a, '---', v.x, v.y).
end().
end(),
Chain('a, b').
result(a + b)
)```
gcd = lambda a,b : [gcd(b, a % b), a][not b]
@hot crypt you meant this? hm I'm not sure I understand what's going on, you have 2 lists there? so?
well, what happens is you create a list and then you index it
it will probably not work here, because a % b will evaluate even if b is 0
anyone ever used a init_subclass and ast to insert arguments into subclass methods?
well yeah
gcd = lambda a,b : [gcd(b, a % b), a][not b]
print(gcd(4,3))
``` throws ZeroDivisionError
ok, i think this works to insert self into a function's signature:
import ast
myfunc = "def test(a): return a"
f = ast.parse(myfunc)
args = f.body[0].args.args
first_arg = args[0]
self = ast.arg(arg='self',
col_offset=first_arg.col_offset,
end_col_offset=first_arg.col_offset + 4,
end_lineno=first_arg.end_lineno,
lineno=first_arg.lineno)
for arg in args:
arg.col_offset += 6
arg.end_col_offset += 6
args.insert(0, self)
compiled_f = compile(f, 'string', 'exec')
locals_={}
exec(compiled_f, {}, locals_)
f = locals_['test']
the goal here is to create a class who, if inherited from, will add ('self') to the method defs for you --- i would also like to add the class's __dict__ to the locals of the method if that's possible
that way you don't even have to use self.whatever_attribute in the method
i did it without ast anyway:
from q import q
class Test(q):
a: _
b: _
c: 1
def tryme():
print(self)
print(a)
and I get no error:
In [87]: a = Test(1, 2, 3)
In [88]: a.a
Out[88]: 1
In [89]: a.b
Out[89]: 2
In [90]: a.c
Out[90]: 3
In [91]: a.tryme()
Test(a=1, b=2, c=3)
1
Guys, I need a context manager, that will make every undefined object be a new instance of a specific class, how on earth can you make that? 
Something with sys.excepthook or settrace?
i can't of a way to do it with context manager, but i could do it with a decorator to a function
assuming the function has the code you want to execute in the with block
well, you could exec that function's code with ChainMap(globals(), defaultdict(lambda:YourObject)) as the globals dict
I am amazed that python does not die when you literally change the type of an object haha
The reason is that it checks to see if it's a safe thing to do first, and if not raises an exception.
@cursive plover what about b or gcd(b,a%b)
@tacit egret can you tell me what's the full code please?
lambda a,b: b or gcd(b,a%b)
that will result in b if b is nonzero
Oh uh
Yeah
so that would be for say 4 and 3: lambda 4,3: 3 or gcd(3,4%3) which is 3
which shouldn't be
gcd = lambda a,b:b and gcd(b,a%b)or a
```this seems to work though
oh btw there's no way to make a lambda function recursive without naming it right? Which is why we're being forced to name it gcd here
was going to suggest this next, haha
gcd = lambda a,b :a if b!=0else gcd(b, a%b) hmm this was what I had earlier
there is using sth like
gcd = lambda a, b:(lambda f, *a: f(f, *a))((lambda f, a, b: b and f(f, b, a%b) or a), a, b)
it's a combinator
^
just so you can make gcd recursive without actually calling gcd
well you're calling f(f, *args) alright but where's what f() does written?
like what it returns or whatever
or is it an alias for gcd?
(lambda f, *args:f(f, *args))(the_fucntion_you_want_recursive, *initial args)
and then in the recursed function you use f to recurse and anything else to return a value
you want to see it in my interpreter