question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

scope issues in comprehensions

See original GitHub issue

Example JSON

{
   "foods": [
      { "name": "carrot" },
      { "name": "banana" }
   ],
   "people": [
      { "name": "alice", "likes": "apples" },
      { "name": "bob", "likes": "banana" },
      { "name": "carrol", "likes": "carrot" },
      { "name": "dave", "likes": "donuts" }
   ]
}

What happens as of 99f6771

$ jello "foods = set(f.name for f in _.foods)
         [p.name for p in _.people if p.likes not in foods]"
jello:  Query Exception:  NameError
        name 'foods' is not defined
        query:  foods = set(f.name for f in _.foods)\n[p.name for p in _.people if p.likes not in foods]
        data:  {'foods': [{'name': 'carrot'}, {'name': 'banana'}], 'people': [{'name': 'alice', 'likes': 'apples'}, {'name': 'bob', 'likes': 'banana'}, {'name': 'carrol', 'likes': 'carrot'}, {'name':
            'dave', 'likes': 'donuts'}]}

$ jello "[p.name for p in _.people if p.likes not in (f.name for f in _.foods)]"
jello:  Query Exception:  NameError
        name '_' is not defined
        query:  [p.name for p in _.people if p.likes not in (f.name for f in _.foods)]
        data:  {'foods': [{'name': 'carrot'}, {'name': 'banana'}], 'people': [{'name': 'alice', 'likes': 'apples'}, {'name': 'bob', 'likes': 'banana'}, {'name': 'carrol', 'likes': 'carrot'}, {'name':
            'dave', 'likes': 'donuts'}]}

What I expected to happen

$ jello "foods = set(f.name for f in _.foods)
         [p.name for p in _.people if p.likes not in foods]"
[
  "alice",
  "dave"
]

$ jello "[p.name for p in _.people if p.likes not in (f.name for f in _.foods)]"
[
  "alice",
  "dave"
]

Suggested change

diff --git a/jello/lib.py b/jello/lib.py
index 60b442c..ed2f4cc 100644
--- a/jello/lib.py
+++ b/jello/lib.py
@@ -459,10 +459,12 @@ def pyquery(_θ_data, _θ_query):
     del _θ_query
     _θ_last = ast.Expression(_θ_block.body.pop().value)    # assumes last node is an expression
 
-    exec(compile(_θ_block, '<string>', mode='exec'))
+    _θ_scope = {'_': _}
+
+    exec(compile(_θ_block, '<string>', mode='exec'), _θ_scope)
     del _θ_block
 
-    _θ_output = eval(compile(_θ_last, '<string>', mode='eval'))
+    _θ_output = eval(compile(_θ_last, '<string>', mode='eval'), _θ_scope)
     del _θ_last
 
     # convert output back to normal dict

Issue Analytics

  • State:closed
  • Created a year ago
  • Reactions:1
  • Comments:5 (3 by maintainers)

github_iconTop GitHub Comments

1reaction
kellyjonbrazilcommented, Jun 14, 2022

Nice - thanks! Yes, that was a struggle to find out how to do get that to work similar to a REPL. I’ll definitely consider the option you describe.

Yes, I was using the strange variable names and del to keep from polluting the scope too much, but also wanted some convenience modules pre-imported like os.environ. I’ll dig into this a bit more. Thanks again!

1reaction
sqwishycommented, Jun 14, 2022

oh one real quick bonus meme because you mentioned this

I feel like I have run into other oddities in the past and this might be the fix for those as well.

so I noticed you remove the last statement from the block and eval it separately in order to get the result of the expression. alternatively, if the last statement in the block is an expression you could leave it in but change it to an assignment statement so it stores to some variable ‘__final’ or something and that will be written to the scope dictionary passed in to exec when the block is exectued. this way there is no need for the eval just the exec on the block

But I don’t actually know if this is better or not. I couldn’t find a situation where this actually improved anything so it maybe doesn’t. Just an idea if the current approach doesn’t work for some reason in the future or whatever.

Read more comments on GitHub >

github_iconTop Results From Across the Web

improper scope in list comprehension, when used in class ...
I believe the problem is that list comprehensions in 3.0 have scope like that of genexprs in 2.5, but the change was deliberate...
Read more >
spec: clarify comprehension scope issues #84 - GitHub
Jon, we follow Python3: a comprehension creates a single lexical block. The first loop's operand is resolved in the enclosing environment. Then ...
Read more >
python - List comprehension rebinds names even after scope ...
As I write new code, I just occasionally find very weird errors due to rebinding -- even now that I know it's a...
Read more >
Variable Scope and List Comprehensions - Jürgen Gmach
Outside the scope of the comprehension, x is no longer defined. Fixed in Python 3, unfixed in Python 3.8#. Python 3.8.10 (default, May ......
Read more >
python comprehensions leak scope again (intermediate ...
old man yells at walrus -- I show some weird (intentional) scoping issues with comprehensions !playlist: ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found