[HN Gopher] This is valid Python syntax
       ___________________________________________________________________
        
       This is valid Python syntax
        
       Author : ankitg12
       Score  : 92 points
       Date   : 2023-06-11 11:10 UTC (11 hours ago)
        
 (HTM) web link (www.bitecode.dev)
 (TXT) w3m dump (www.bitecode.dev)
        
       | EddieJLSH wrote:
       | Fun syntax gotcha that I saw recently was a YAML MAC address that
       | were all numbers like 11:22:33:11 which Python was parsing as a
       | sexagesimal number so some libraries were throwing type errors
        
       | dec0dedab0de wrote:
       | Oh man, I seriously just wretched at the idea of putting an
       | import in a lambda. That was sick, and awesome.
        
         | Waterluvian wrote:
         | Python has so much potential for doing the most horrible
         | things. I love it. I love seeing what people come up with.
        
           | gonzo41 wrote:
           | Until you find them in production. Then you git blame and go
           | take a long look in the mirror thinking about what you've
           | done.
           | 
           | In the early aughts the terseness of python seemed great. Now
           | as I age I find i have less time for it.
        
             | IshKebab wrote:
             | I don't think terseness is the issue. It's the fact that it
             | lets you do anything, even if it's a terrible idea.
             | `__subclasses__` is my favourite example of that.
        
             | Waterluvian wrote:
             | I feel the exact same way. Python gave me so much
             | flexibility and escape hatches when I did dumb design
             | things as a novice. Now as a veteran I just want a boring,
             | strong static language with clear boundaries and
             | behavioural expectations
        
               | sureglymop wrote:
               | I just always found it nice as a prototyping language. If
               | you have an idea of how you want to do something or solve
               | some problem, create a working prototype in python. Then
               | create a more polished version in another language.
        
       | pedrovhb wrote:
       | > While not as powerful as destructuring in other languages (I
       | still wish we could unpack dicts as JS does with objects)
       | 
       | Like this?                   In [1]: a = {1: 10, 2: 20}
       | In [2]: b = {3: 30, **a}              In [4]: b         Out[4]:
       | {3: 30, 1: 10, 2: 20}
       | 
       | Or pattern matching?                   In [6]: match b:
       | ...:     case {3: somethingelse, 1: foo}:            ...:
       | print(somethingelse, foo)         30 10
        
         | babel_ wrote:
         | I think they mean {a: x, b: y, *c} = d, which is a nice idea
         | now we have a stable order to dicts, but I've rarely come
         | across times when I needed this that I can't simply use
         | .items() and such for, or care about more general pattern
         | matching like values for specific keys, which you can just do
         | a, b = d["a"], d["b"] with little loss of clarity, sure {"a":
         | a, "b": b} = d is cute and all, and maybe we'll oneday see it
         | now we have match as syntactic precedent, but I don't really
         | feel concerned by its absence.
        
         | tuukkah wrote:
         | They mean you can unpack lists:                 a, b, *rest =
         | args
         | 
         | So why not dicts:                 "a": a, "b": b, **rest =
         | kw_args
         | 
         | Also, in JS the destructuring does not fail because of extra
         | elements even if you don't capture them. Further, in JS you
         | don't need the quotes, and a: a can be shortened as simply a.
         | You'll need the dict curly braces though. Thus the same in JS,
         | which you see a lot e.g. for properties of a component in React
         | code:                 { a, b } = props;
         | 
         | It's quite nice with no excessive "line noise" and even makes
         | up for JS functions lacking real keyword arguments.
        
       | bandrami wrote:
       | As a Perl guy none of that is even slightly distressing to me
        
       | 1024core wrote:
       | Python is slowly morphing into Perl... :-D
        
       | bionhoward wrote:
       | Lol, your definition of "valid syntax" is wayyy different from
       | mine. I wouldn't ever use crap like that, it's too unreadable to
       | be "valid syntax" even if the interpreter can handle it. Can we
       | all collectively agree to include our own brains in the
       | definition of "valid code?"
        
       | dinom wrote:
       | Lol, and they say Perl is bad.
       | 
       | https://www.perl.com/pub/2000/01/10PerlMyths.html/#Perl_look...
       | 
       | This isn't meant as flamebait, I'm just pointing out you can
       | write obfuscated code in any language.
        
       | ptx wrote:
       | Strange article. I thought it was going to show that these
       | features interact in some unexpected way that causes weird bugs,
       | but it all seems to work as expected.
       | 
       | So the conclusion, I guess, is that code doesn't make a whole lot
       | of sense when it uses every available feature for no particular
       | reason and foreign languages for variables names?
        
         | raincole wrote:
         | This article just reminds me how unexpected JavaScript is,
         | unlike Python.
        
         | BiteCode_dev wrote:
         | Author here.
         | 
         | There is no conclusion to the article, the weird syntax is just
         | a mean to an end:
         | 
         | - This makes people curious, so they read.
         | 
         | - This gives an opportunity to show a lot of cool features of
         | the language in a short format. As the post mentions, Python's
         | learning curve is smooth but long, so it's nice to be able to
         | introduce some tricks to coders that would otherwise not be
         | exposed to them.
         | 
         | - This demonstrates those things indeed, as you said, work
         | together. Even if Python is a language on which a lot of things
         | have been bolted on after the fact, it's still decently
         | integrated.
         | 
         | - This allow me to insert a warning about not abusing them
         | because even if Python is a readable language, you can always
         | go too far. It's good maintain this message in the community.
         | 
         | - It's just fun, really. It's an article I would enjoy reading.
        
           | babel_ wrote:
           | Must admit I wasn't quite sure what the point of the first
           | one was. 0xBEAF is literally just 48815, as is 0x_B_E_A_F, as
           | the numeric literals now ignore interleaved "_", and you just
           | format it as a float after-the-fact as normal in an f-string.
           | So why? There's nothing particularly special in the use of
           | syntax here, so hardly a curious or weird artifact, let alone
           | abuse.
           | 
           | > * has a completely different meaning
           | 
           | I'd personally rephrase this, as the idea around splatting is
           | that it symmetrically can "gather" and "spread" multiple
           | values, albeit with the syntactic limtations of starred
           | expressions (likewise with *).
           | 
           | You do seem to confuse syntax for semantics: [][:] = [...] is
           | a semantic trick to avoid binding a sequence, if you did a =
           | [][:] = [1,2] you'd find a == [1,2] because it just passes
           | along the reference to the right hand side, so without
           | another layer it's just skipping binding it, and that's the
           | major trick with the second one, being that it "discards" the
           | first value when destructuring this way (so long as that
           | first value is a sequence). This can be done "better" with _,
           | *_ destructuring, which probably weirds more people out to
           | discover that shadowing in a statement is legal (and the
           | unused/shadowed variable is "better" as it works even when
           | the first value isn't a sequence, making [][:] more fragile).
           | People with knowledge of other languages might get confused
           | by _ being a variable, but that's a known stumbling block.
           | 
           | Naturally, everyone has their own journey through discovering
           | Python (or whatever language) and may or may not touch upon
           | many parts of it syntactically and semantically. However, as
           | far as "weird" syntax that has valid semantics goes, I'd say
           | this is far from esoteric or unusual (where's the walrus?),
           | and some parts are just the natural expression (such as with
           | the splatting). Really, the only code smell I find here is
           | the inlining of everything and redundancy (i.e. splatting a
           | generator into a list)!
        
           | agumonkey wrote:
           | as a semi plt nerd, i like to push grammars a bit deeper than
           | average application, thanks
           | 
           | and often indentation helps keeping track of structures
        
           | [deleted]
        
       | BoppreH wrote:
       | I don't find that so bad. They look complicated but behave as
       | expected, and would only be a problem if you inherit a code base
       | from a lone wolf without common sense.
       | 
       | Python has plenty of warts[1], but I don't think the syntax is
       | one of them (yet). Some of the more concerning warts:
       | # Mutable defaults in function parameters.         def
       | fn(arg=[]):             arg.append(1)             print(arg)
       | fn() # [1]         fn() # [1, 1]         fn() # [1, 1, 1]
       | # Loop variables are shared across executions.
       | lambda_list = [lambda: print(i) for i in range(5)]         for fn
       | in lambda_list: fn()         # 4         # 4         # 4
       | # 4         # 4              # Iterating over an exhausted
       | generator yields an empty list.         numbers = (i for i in
       | range(5))         assert 1 in numbers
       | print(list(numbers))         # []         # I hate this one
       | because it could have easily been an error, it's          # hard
       | to track down, and lots of stdlib functions return generators.
       | # Chained operators can sometimes be very unintuitive.
       | should_be_ascending = True         assert 1 < 2 ==
       | should_be_ascending         # AssertionError because it was
       | interpreted as         # `(1 < 2) and (2 == should_be_ascending)`
       | 
       | [1]: https://github.com/satwikkansal/wtfpython
        
         | [deleted]
        
         | babel_ wrote:
         | I agree that functions sharing the references to their
         | arguments is awkward (though useful on occassion), however the
         | alternative is to re-instansiate the arguments by a shallow
         | copy, a deep copy, or full-on re-evaluation, all of which have
         | their problems and are significantly slower (calls are already
         | slow in Python as is!) so it makes sense on balance for many
         | use cases, especially as most defaults in practice are
         | immutables like numbers and strings (so it's really just the
         | usual warts for references to mutables).
         | 
         | The others are a little less concerning to me. Sure, the lack
         | of fine-grained scoping is annoying, but that's just how Python
         | operates; the example confuses the point via the lack of
         | variable capture in lambdas, when fn is called it does a normal
         | locals/globals lookup for i, and the lack of scope means the
         | last value of i spills outside of lambda_list, meaning all
         | evaluate to 4, but this is primarily a problem with how lambda
         | works without capture (again, scoping in Python is what it is).
         | I've even begrudgingly (ab)used Python's lack of scoping on
         | occassion, albeit mindfully aware of when I can and when it is
         | actually defined, because it is very easy to cause problems
         | with and generally best avoided.
         | 
         | Empty generators to empty lists isn't a huge concern in my
         | experience, as it lines up with empty lists, sets, and dicts as
         | the behaviour for comprehensions and how to handle them when
         | there's nothing. Also, your example for it, uh, doesn't work as
         | you say (it prints [2,3,4]).
         | 
         | Operator precedence is always awkward in every langauge that
         | has it, and operators are still awkward in those that don't,
         | and Python's is no exception, though it's largely better than
         | C's -- in Python, comparisons share precedence and are lower
         | than most other operators. I feel there isn't really a right
         | answer to the problem as is, though ==/!=/in/is maybe shouldn't
         | be part of chaining (but chaining is defined over all
         | comparison operators [1] so that's probably not going to
         | change). As always, when in doubt, use parenthesis.
         | 
         | [1]:
         | https://docs.python.org/3/reference/expressions.html#compari...
        
         | crabbone wrote:
         | Syntax is pretty much one of the downsides of Python.
         | Especially the new additions, including f-strings, match-case,
         | type annotations, async-await, data-classes etc. The largest
         | downside is that they hugely blew up the language complexity
         | for no tangible gains. It's like you had a crappy bicycle
         | before, but after an "upgrade", you have a crappy bicycle that
         | doubles as a crappy ice-cream maker.
         | 
         | This means that anyone who wants to write a parser for Python
         | from scratch is screwed. The language was garbage before, and
         | writing a parser for Python was already very difficult, but
         | after more junk was added to it, it just became unnecessarily
         | more difficult. So, if you might have had a glimmer of hope to
         | have tools that analyze the source code and do something with
         | it, even as trivial as highlighting... well, there's less and
         | less hope of that happening.
         | 
         | So, OPs complaint about f-string is sort of valid. OP didn't
         | really look for a very bad example though, but bad examples are
         | hard to refine into more concise form. Fundamentally, what
         | sucks about f-strings beyond them being completely unnecessary
         | is the situations where one has to deal with multiple
         | incoherent escaping rules, s.a. when one has to interface with,
         | eg. logging module or string.Template or str.format() or % in
         | general. Another vomit-inducing combo is raw strings combined
         | with f-strings (i.e. building regular expressions through
         | interpolation).
         | 
         | Yet another downside of f-strings is the departure from object-
         | oriented approach. The benefit of str.format() was it being
         | object-oriented, which allowed for extensions in the same way
         | objects can be extended. f-strings are an extension of the
         | language syntax, but Python doesn't have an easy general way to
         | extend its syntax (barring the fact that you can abuse the
         | import system to import altered Python sources s.t. they have
         | desired syntax).
         | 
         | But, again, these disadvantages are hard to present as a one-
         | liner, and for many simple uses of the language, which is the
         | majority of its uses they are inconsequential. Unfortunately,
         | that majority of uses is also transient and contributes nothing
         | back to the language... so, it could be confusing to rely on
         | the majority's opinion when it comes to judging the quality of
         | various language features.
        
           | mixmastamyk wrote:
           | You don't write a parser from scratch, you use the ast
           | module.
           | 
           | The rest of this is excessive negativity trying to sound
           | superior while ignoring history.
           | 
           | (I even agree about the match statement and walrus, but
           | fstring is a very useful compromise.)
        
             | otherme123 wrote:
             | I'm using walrus a lot, can't find any negative: it's clear
             | what it does, and the code feels cleaner.
        
         | gcbirzan wrote:
         | First one is annoying, but it makes sense, to some degree. You
         | say the default is this particular instance that gets created
         | when you define the function.
         | 
         | Second is just how things work, the function is a closure that
         | evaluates the non local variable when you run it.
         | 
         | Third one... Iterating over the generator does NOT yield an
         | empty list, it raises StopIterator. As does trying to iterate
         | after a generator is done, so there's no way for list to know
         | what's happening. You can argue that's a bad design, but I'm
         | not so sure, an iterator is something you need to make sure you
         | don't try to iterate over twice...
         | 
         | I don't get the 4th. Comparison operators have the same
         | precedence, so yeah.
        
           | BoppreH wrote:
           | > First one is annoying, but it makes sense, to some degree.
           | You say the default is this particular instance that gets
           | created when you define the function.
           | 
           | Could just as easily be an expression that's reevaluated on
           | each call. In fact it's what most other languages do, making
           | it twice as bad a decision. Even Javascript gets it right:
           | function fn(arg=[]) { return arg; }         fn() === fn()
           | // false
           | 
           | > Second is just how things work, the function is a closure
           | that evaluates the non local variable when you run it.
           | 
           | That's again a decision made by Python. Some other languages
           | behave as if each iteration had declared a different
           | variable. Here's Javascript again, you can even use `const`
           | for the iterating variable:                   const fns = [];
           | for (const i of [1, 2, 3]) {             fns.push(() =>
           | console.log(i));         };         fns.forEach(fn => fn());
           | // 1         // 2         // 3
           | 
           | > Third one... Iterating over the generator does NOT yield an
           | empty list, it raises StopIterator.
           | 
           | Sorry, my phrasing was imprecise. It's not doing 'yield []',
           | but an exhausted generator does behave like an empty
           | generator. Maybe they could have made it so the first
           | exhaustion raises StopIterator, and subsequent ones a
           | different exception? Like reading from a closed channel.
           | 
           | > I don't get the 4th. Comparison operators have the same
           | precedence, so yeah.
           | 
           | It's not precedence, it's Comparison Chaining, the same
           | feature that enables '1 < a <= 10'. If the operators had
           | simple precedence, it would be equivalent to '(1 < a) <= 10'
           | or '1 < (a <= 10)', but Comparison Chaining evaluates it as
           | '(1 < a) and (a <= 10)'. Useful for specifying ranges, but a
           | foot gun in other scenarios, like in my example.
        
           | gpm wrote:
           | > Second is just how things work, the function is a closure
           | that evaluates the non local variable when you run it.
           | 
           | In python, yes. In other languages, not necessarily. There
           | are two ways that this could reasonably not be the result:
           | 
           | 1. For i in iterator could create a new variable i (shadowing
           | the old i) on each iteration of the loop. This isn't how
           | python works (which also means you can do things like access
           | i after a for loop terminates) but there are languages that
           | work that way.
           | 
           | 2. Capturing an integer could _copy_ that integer, instead of
           | copying a pointer to a value that can change in the future.
           | Again, not how python works, but how some languages work.
           | 
           | The first in particular I think is more intuitive in other
           | languages, and leads to other footguns in python as well,
           | e.g.:                   for i in range(5):             for i
           | in range(10):                 pass
           | print("Finished loop iteration", i) # I meant 0, 1, 2, 3, 4.
           | Instead I got 9 9 9 9 9
        
           | accoil wrote:
           | I can see how it happened, but the first one does not make
           | sense. Most people would expect argument defaulting to be per
           | execution. If the argument is None, instantiate it with this
           | new value.
           | 
           | I'm curious what making it shared and mutable achieves.
        
         | travisjungroth wrote:
         | On the generator example, it doesn't yield an empty list. It
         | continues to return an empty iterator. This couldn't have
         | easily been an error because one aspect of generators is they
         | can be dynamic. There could be an API call in there, where it
         | polls for new results. Raising a "NoMoreForever" error or
         | something would break the interface.
        
           | BoppreH wrote:
           | You're right, "empty list" is an imprecise description. But
           | couldn't they have made it behave like Go channels, for
           | example? Read it until exhaustion, then close it, so that
           | subsequent reads (or calls to 'next()') fail with something
           | other than StopIterator.
           | 
           | I know it's not backwards compatible and hence a no-go now,
           | but the original decision puzzles me.
        
         | [deleted]
        
         | formerly_proven wrote:
         | 1 and 2 are the scoping rules (since Python has no variable
         | declarations [1], making all blocks scopes would be pretty
         | awkward and require tons of "xy=None # reassigned later" and
         | nonlocals), 3 should result in [2, 3, 4], not [].
         | 
         | [1] It actually does nowadays, ever since non-assigning
         | annotations came around.
        
         | BoppreH wrote:
         | Correction, the empty iterator example should have been `assert
         | 5 not in numbers`, or another expression that exhausts the
         | generator.
         | 
         | And to everyone saying that the behaviors are correct because
         | of scoping rules, or iteration protocol: I know. Those are not
         | compiler bugs, but bad design decisions. Every example here has
         | a more intuitive behavior in another language.
        
       | actionfromafar wrote:
       | Someone(tm) should make a Python-to-Python compiler which
       | converted all normal constructs into weirdness like this.
        
         | uncharted9 wrote:
         | For me, one of chatGPT's most frequent uses is to convert all
         | of these one-liner statements into logical multi-line code that
         | my feeble brain can process.
        
       | lysecret wrote:
       | Biggest footgun in python IMO are mutable default parameters. It
       | amazes me that someone thought this was a good idea.
        
         | lysecret wrote:
         | Also a most random note here:
         | 
         | mutable /'mju:t@bl/ adjective liable to change.
         | 
         | I like the emphasis that it's liability haha
        
         | Spivak wrote:
         | That's not really what's going on. Python has default values
         | where, say Ruby, has default expressions.                   def
         | myfunc(x=[])           puts x         end
         | 
         | In Ruby is the same as                   def
         | myfunc(x=lambda:[])           print(x())
         | 
         | in Python when the default is needed. The benefit is
         | performance, you only have to evaluate the default once at
         | function definition time. I won't say it's not a footgun but
         | it's also I think a sane choice when in Python it's common to
         | have functions with many default parameters.
        
           | o1y32 wrote:
           | "the benefit is its performance"
           | 
           | Sure, but when you design a language you are striking a
           | balance between many things (which is why some features could
           | take years to finalize and implement). In this case, the gain
           | in performance is likely minimal, but the confusion and
           | surprise it causes does a lot of damage.
           | 
           | (I recently read through the entire series of conversation
           | with Anders Hejlsberg on the design of C# which is very
           | insightful https://www.artima.com/intv/anders.html)
        
         | sfink wrote:
         | For lists, use a tuple as default, since it's immutable.
         | def foo(allowed=()):           truthy = ['true', 'True']
         | truthy += allowed # most things work
         | allowed.append('yes') # error
         | 
         | It's not perfect (`truthy = ['true', 'True'] + allowed` will
         | _not_ work).
        
           | MawKKe wrote:
           | while that works, it does not fully document what is expected
           | from caller. And AFAIK using tuples like that is not very
           | common. I'd prefer something like:                 def
           | foo(allowed: Optional[List[ElemType]] = None):
           | allowed = allowed or []         ...
        
         | crabbone wrote:
         | You are too naive when you think that someone _thought_ about
         | it. Many things in Python happened because that 's how they
         | were implemented initially _without_ much thinking or at all.
         | 
         | There was no plan, and, in the initial stages, I'd imagine that
         | the author(s) were surprised that they'd even gotten this far.
         | 
         | Famously, the whole object system in Python was implemented in
         | like a few days. That included the design and testing too... It
         | did see some changes over time, but there's a lot of petrified
         | turd there that's increasingly difficult to get rid of.
        
       | macintux wrote:
       | Unpacking used to be more powerful. I was mostly oblivious to the
       | Python community when I started working on a project and
       | discovered that you could unpack tuples in function heads, like
       | Erlang, and started using that frequently.
       | 
       | Then I discovered I was using Python 2 and it was EOL, and that
       | Python 3 had dropped support for that "because no one uses it."
       | 
       | Maddening.
        
       | bettercallsalad wrote:
       | We had a dev in our team who would love to use these one liner
       | cryptic expressions and challenge the minds of code reviewers.
       | 
       | It was all fun until he left and others had to refactor bunch of
       | code. One liners like that often have very little benefits IMHO.
        
       | svilen_dobrev wrote:
       | most of these are loooong time there. list[:] replacement is
       | since ever. (used for reversing a list, when there wasnt .reverse
       | method)
       | 
       | But my beloved one, is the sequence of these changes, in time:
       | 
       | ver.134: x = ('a', 2)
       | 
       | some change in requirements made 2nd value unneeded, so it
       | became:
       | 
       | v167: x = ('a',)
       | 
       | which is one day, "optimized" to:
       | 
       | v193: x = ('a')
       | 
       | Funny thing is, and x[0] will still deliver 'a' as it was before.
       | Now if that 'a' is not constant but some parameter, and it's not
       | 1 character long string..
        
       | 2h wrote:
       | > a, b = b, a
       | 
       | > (don't know why this is so famous, I never, ever used that in
       | prod)
       | 
       | This allows you to sort an array in place. It's heavily used in
       | Go:
       | 
       | https://godocs.io/sort#example-package
        
         | roelschroeven wrote:
         | A variant is useful to update two (or more) dependent variables
         | in one go without having to introduce temporary variables. For
         | example to calculate the greatest common divisor using the
         | Euclidean algorithm:                   def gcd(a, b):
         | while b > 0:                 a, b = b, a % b             return
         | a
        
         | babel_ wrote:
         | Swapping variables around is also really handy for patterns
         | like "alter list a until stable", especially with the walrus :=
         | operator allowing you to avoid declaring b outside of the while
         | loop, keeping the intent clearer.
        
         | BiteCode_dev wrote:
         | Yeah but in Python list.sort() takes care of that.
        
           | falcor84 wrote:
           | True, but every once in a while we want to sort something
           | that isn't a list and/or with a custom sorting algorithm.
        
         | tgv wrote:
         | You can always write something like {var tmp=a;a=b;b=tmp;} in
         | any language if you want to sort in place.
        
           | 2h wrote:
           | > in place
           | 
           | that doesn't mean what you think it means.
        
             | Etherlord87 wrote:
             | Do you know what it means? It seems irrelevant to the
             | discussion, introducing a new variable won't make you
             | unable to sort in place. You could even allocate memory on
             | the heap and still sort in place.
        
               | 2h wrote:
               | > Do you know what it means?
               | 
               | yeah, it means no temporary variable.
        
           | Etherlord87 wrote:
           | The difference here is, a,b=b,a will use one less bytecode,
           | by taking advantage of ROT_TWO
        
       | jonnycomputer wrote:
       | God, is this going to be what I see in my next python coding
       | interview?
        
         | iblaine wrote:
         | Idea for a new linter; throw a warning if it sees extreme non-
         | intuitive uses of Python. Given the trajectory of Python
         | improvements over the past few years, this linter may not be
         | far off.
        
         | Sosh101 wrote:
         | If so, then I'd be happy to fail.
        
       | guenthert wrote:
       | > Inverting variables:
       | 
       | Non-native speaker here and even after 20+years every once in a
       | while it bites, but I know that as 'swapping variables'. Where
       | does the inverting come from? Related to the 'inverting a tree'?
        
         | coke12 wrote:
         | It's possible they are trying to avoid making people think of
         | memory swapping which is existing jargon and usually means
         | something more complex.
        
       | charcircuit wrote:
       | Shu Zi  is numeral. It doesn't make sense as a translation for
       | numbers in that function.
        
       | randallsquared wrote:
       | > _if you use "_" in hex, and interpolate it with a 2 decimal
       | precision_
       | 
       | ...or if you don't. The '_' doesn't change anything or add
       | anything, here.
        
       | kkirsche wrote:
       | I learned about Python dev mode in this, thanks!
       | 
       | For anyone else:
       | 
       | https://docs.python.org/3/library/devmode.html
        
       ___________________________________________________________________
       (page generated 2023-06-11 23:01 UTC)