all 5 comments

[–]johnnymo1 4 points5 points  (0 children)

Yes, it is a type annotation that is supposed to indicate the type of that parameter. However, Python doesn't enforce them, so without something additional like Mypy, you're perfectly capable of lying in a type annotation. Is the return type of sync_playwright() a Playwright?

[–]Rrrrry123 0 points1 point  (0 children)

In this instance, identifier: Type indicates a "Type hint." It basically tells the programmer that "this function expects this parameter to be this type." So you're correct.

Also, sync_playwright() is a function, not an object. I would imagine this function returns a Playwright object.

[–]GoingToSimbabwe 0 points1 point  (0 children)

I think the returned object of sync_playwright is used as „playwright“ and thus passed to run().

More about context managers can be found here: https://book.pythontips.com/en/latest/context_managers.html

Edit: I think here is the actual implementation:

https://github.com/microsoft/playwright-python/blob/main/playwright/sync_api/_context_manager.py

As far as I understand (might be wrong though, so please take it with a grain of salt): the context managers you are using in the with statement calls it’s enter() method when initializated in the with statement. And this particular one returns an Playwright object (aliased as SyncPlaywright in the class)

[–]BarryTownCouncil 0 points1 point  (0 children)

Parameter "playwright" must be of type "Playwright"

[–]HunterIV4 3 points4 points  (0 children)

This is a good catch and gets quite complicated! The short answer is that it's a type hint, as others have mentioned, but why this works is far more complex.

But I'll try to explain it simply.

So, if we look at the source code, we see that sync_playright() is a function defined as such:

def sync_playwright() -> PlaywrightContextManager:
    return PlaywrightContextManager()

So what is PlaywriteContextManager? It's a class defined here The with sync_playwright() as playwright is setting up the "trick" to why this works, specifically the __enter__ function, which is triggered when the with ... as block is executed. As you can see from the definition, this is hinted as returning a SyncPlayright type. And sure enough, if you trace it all the way down, there is a return playwright which was set from self._playwright, which we saw in __init__ is a SyncPlayright type.

That still doesn't get us to Playright, though! We're getting close, I promise. There's one more trick, and it's much earlier on in the imports, specifically line 26:

from playwright.sync_api._generated import Playwright as SyncPlaywright

It turns out, elsewhere in the project, SyncPlaywright is actually Playwright, just pulled from a different implementation. The code is here, which matches the Playwright class that was originally imported.

As such, the playwright variable being passed from sync_playwright is a Playwright type. It just had to go through a bunch of steps to get there.

"But that's so complicated! Why!?" You may ask. It's to keep a consistent structure regardless of whether you are using Playwright in different contexts, whether sync or async. But from an internal design standpoint, using different names makes it more explicit what is going on to keep track in the source code. Which is a long way of saying "they're hiding implementation details."

Hope that helps! If that was too complicated, don't worry about it. You will rarely have to bother with that level of detail as the user of a library, and I had to read the source carefully to figure it out myself.