markdown: Language Server does not find extension attribute: `Cannot access member "Meta" for type "Markdown" Member "Meta" is unknown`

When using the meta extension, the Meta field of the Markdown class is not found by Pyright. Perhaps I missed something, can we import a stub file to make this error go away?

For clarity: The code works, but the language server does not find the Meta Member for type Markdown.

This might also happen with the attributes added by other extensions, but I’ve found it for the meta extension specifically.

Minimal example: test.py:

import markdown
MD = markdown.Markdown(extensions=['meta'])
with open("foo.md", "r") as f:
    content: str = f.read()
    html: str = MD.convert(content)
    meta = MD.Meta    # Cannot access member "Meta" for type "Markdown"    Member "Meta" is unknown
    print(meta)

foo.md:

key: 19

this is the content

executing the script:

$ python test.py           
{'key': ['19']}

Proposed Fix Add a python stub file for the Meta attribute.

About this issue

  • Original URL
  • State: open
  • Created 9 months ago
  • Comments: 20 (15 by maintainers)

Most upvoted comments

  1. Declare the 3 core attributes. … Adding third-party extensions there will be summarily rejected.

The problem is that this favors the built-in extensions, which I have tried to avoid. I have tried to maintain the idea that anything that a built-in extension can do a third-party extension can do. Yes, I know the built-in extensions are favored just by being built-in, but for many years now I have operated on the assumption that in the future each built-in extension could eventually be broken out into a separate third-party extension. I’m not inclined to change that now.

  1. Trick the typecheckers by defining __getattr__ and letting it return Any. Then all type checking will be shut down for accessing unknown attributes on Markdown and for any values obtained from them.

To me this seems like the least bad option of those listed. As a benefit, it provides an official API for any extension (first or third party) to make data available to the user. Obviously, option 3 would be preferred for that purpose (official API), but the backward incompatibility is a blocker… unless we combined options 2 and 3.

We could go with option 3 and add an attribute which stored anything by key as an official API. Then we could add a getter as glue between the old and new. The getter should be generic to work with any unknown third-party extensions as well. Let’s call this option 5.

Regardless of whether we went with options 2, 4, or 5 (the only contenders IMO), there would be no actual type checking. I’m okay with that. However, 2 or 5 would at least give us a way to satisfy the type checkers. Option 4 would not, but maybe that doesn’t matter…

By the way, the type checking of both pyright and mypy are not based on the markdown package, but rather the types-Markdown package

Good point. Technically, at this time, this issue should have been raised with @python/typeshed. And yes, it looks like it may remain there for now. @PlexSheep you should probably submit an issue there if you would like an immediate resolution.

Regardless, I am going to leave this open until a decision is made regarding what we are going to do here.

Unless you define a Meta property upfront and type it, it will show up as a “mystery” attribute.

Ah okay, so this is the complaint then.

It recently occurred to me that we could provide some hook for extensions to use for this. For example, we could provide an attribute which stored key/value pairs where the key would be a name provided by the extension and the value would be whatever data the extension provides. Then it could be type annotated as dict[str, Any] (or whatever format we went with).

The issue of course is that many existing extensions already provide their own custom solutions and this would be a new API. Users would then need to alter their code to check the new location. Maybe we could use a custom getter and setter which would cause the extension to write to our custom location and the user to read from our custom without any changes on their end (extension or user).

Although, now that I think about it, some projects make extensive use of custom getters and setters on classes (see Django for example). They have a whole collections of potential attributes which are unknown. How do they handle this? Maybe that is the approach we should take.

Well, you are documenting the API, yes, but there are no type annotations.

Yes, we are adding type annotionations, which is my point. It will no longer be true that the library does not provide type annotations. However, I’m not really familiar with LSP or what is does or what it expects, so I don’t know how this is relevant.

I don’t think the argument is that md.Meta doesn’t exist, only that LSP (Language Server Provider) in their editor can’t automatically recognize md.Meta and its type.

As Python Markdown does not provide type annotations, and any that are available are provided by 3rd parties, I’m not sure if this is an issue for Python Markdown. If we were currently providing type annotations, then maybe it could be argued we should do something, but currently, this is not a typed library, even if someone else has provided typing that you are using in conjunction with your LSP.