Static Type Checking: Enums - #2153

Merged
juergbi merged 2 commits into
apache:masterfrom
nathanwilliams-ct:nathan/enum-static-type-checking
Jul 28, 2026
Merged

Static Type Checking: Enums#2153
juergbi merged 2 commits into
apache:masterfrom
nathanwilliams-ct:nathan/enum-static-type-checking

Conversation

@nathanwilliams-ct

Copy link
Copy Markdown
Contributor

Buildstream implements a custom FastEnum[1].
mypy only supports enum.Enum (and it's official variations) when it comes to doing static type checks on enums [2].
We can't subclass Enum in FastEnum to make mypy happy, because Enum doesn't allow subclassing [3].
So we end up with a bunch of str, int etc around the codebase,
instead of the appropriate Enum classes like OverlapAction or _Scope which are usually recorded separately in the doc strings.
This means we don't get proper static type checking on our Enums :( .
With stub files [4][5] we can lie to mypy about what type the Enum classes are[6],
so the type hints around the codebase are now correct be corrected.
Now we can have proper static type checking on the custom Enums, Yay!

So

class OverlapAction(FastEnum):
ERROR: str
WARNING: str
IGNORE: str

gets stubbed as

from enum import Enum
class OverlapAction(Enum):
ERROR: str
WARNING: str
IGNORE: str

[1] src/buildstream/types.py#L32
[2] https://mypy.readthedocs.io/en/stable/literal_types.html#enums
[3] https://docs.python.org/3/howto/enum.html#restricted-enum-subclassing
[4] https://typing.python.org/en/latest/guides/writing_stubs.html
[5] https://mypy.readthedocs.io/en/stable/stubgen.html
[6] python/mypy#3217

Buildstream implements a custom `FastEnum`[1].
mypy only supports `enum.Enum` (and it's official variations) when it comes to doing static type checks on enums [2].
We can't subclass Enum in FastEnum to make mypy happy, because Enum doesn't allow subclassing [3].
So we end up with a bunch of `str`, `int` etc around the codebase,
instead of the appropriate Enum classes like `OverlapAction` or `_Scope` which are usually recorded separately in the doc strings.
This means we don't get proper static type checking on our Enums :( .
With stub files [4][5] we can lie to mypy about what type the Enum classes are[6],
so the type hints around the codebase are now correct be corrected.
Now we can have proper static type checking on the custom Enums, Yay!
So
```
class OverlapAction(FastEnum):
ERROR: str
WARNING: str
IGNORE: str
```
gets stubbed as
```
from enum import Enum
class OverlapAction(Enum):
ERROR: str
WARNING: str
IGNORE: str
```
[1] src/buildstream/types.py#L32
[2] https://mypy.readthedocs.io/en/stable/literal_types.html#enums
[3] https://docs.python.org/3/howto/enum.html#restricted-enum-subclassing
[4] https://typing.python.org/en/latest/guides/writing_stubs.html
[5] https://mypy.readthedocs.io/en/stable/stubgen.html
[6] python/mypy#3217

@juergbijuergbi left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Ideally, we'd have a small script to automate the generation of type.pyi and then we could run that in CI to verify that it's up-to-date. However, I think regeneration is reasonably simple as is and there shouldn't be a functional risk with an outdated type.pyi, so I think it's fine to merge this as is.

@juergbi
juergbi merged commit ad6b437 into apache:masterJul 28, 2026
18 checks passed
return cls._value_to_entry[value]
except KeyError:
if type(value) is cls: # pylint: disable=unidiomatic-typecheck
if isinstance(value, cls): # pylint: disable=unidiomatic-typecheck

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AFAICT your are now doing this right.

https://pylint.pycqa.org/en/latest/user_guide/messages/convention/unidiomatic-typecheck.html

Suggested change
ifisinstance(value, cls):# pylint: disable=unidiomatic-typecheck
ifisinstance(value, cls):

So you dont need to disable the lint.

Comment threadsrc/buildstream/_pipeline.py
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@nathanwilliams-ct@juergbi@wsalmonct
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Static Type Checking: Enums - #2153

Merged
juergbi merged 2 commits into
apache:masterfrom
nathanwilliams-ct:nathan/enum-static-type-checking
Jul 28, 2026
Merged

Static Type Checking: Enums#2153
juergbi merged 2 commits into
apache:masterfrom
nathanwilliams-ct:nathan/enum-static-type-checking

Conversation

@nathanwilliams-ct

Copy link
Copy Markdown
Contributor

Buildstream implements a custom FastEnum[1].
mypy only supports enum.Enum (and it's official variations) when it comes to doing static type checks on enums [2].
We can't subclass Enum in FastEnum to make mypy happy, because Enum doesn't allow subclassing [3].
So we end up with a bunch of str, int etc around the codebase,
instead of the appropriate Enum classes like OverlapAction or _Scope which are usually recorded separately in the doc strings.
This means we don't get proper static type checking on our Enums :( .
With stub files [4][5] we can lie to mypy about what type the Enum classes are[6],
so the type hints around the codebase are now correct be corrected.
Now we can have proper static type checking on the custom Enums, Yay!

So

class OverlapAction(FastEnum):
ERROR: str
WARNING: str
IGNORE: str

gets stubbed as

from enum import Enum
class OverlapAction(Enum):
ERROR: str
WARNING: str
IGNORE: str

[1] src/buildstream/types.py#L32
[2] https://mypy.readthedocs.io/en/stable/literal_types.html#enums
[3] https://docs.python.org/3/howto/enum.html#restricted-enum-subclassing
[4] https://typing.python.org/en/latest/guides/writing_stubs.html
[5] https://mypy.readthedocs.io/en/stable/stubgen.html
[6] python/mypy#3217

Buildstream implements a custom `FastEnum`[1].
mypy only supports `enum.Enum` (and it's official variations) when it comes to doing static type checks on enums [2].
We can't subclass Enum in FastEnum to make mypy happy, because Enum doesn't allow subclassing [3].
So we end up with a bunch of `str`, `int` etc around the codebase,
instead of the appropriate Enum classes like `OverlapAction` or `_Scope` which are usually recorded separately in the doc strings.
This means we don't get proper static type checking on our Enums :( .
With stub files [4][5] we can lie to mypy about what type the Enum classes are[6],
so the type hints around the codebase are now correct be corrected.
Now we can have proper static type checking on the custom Enums, Yay!
So
```
class OverlapAction(FastEnum):
ERROR: str
WARNING: str
IGNORE: str
```
gets stubbed as
```
from enum import Enum
class OverlapAction(Enum):
ERROR: str
WARNING: str
IGNORE: str
```
[1] src/buildstream/types.py#L32
[2] https://mypy.readthedocs.io/en/stable/literal_types.html#enums
[3] https://docs.python.org/3/howto/enum.html#restricted-enum-subclassing
[4] https://typing.python.org/en/latest/guides/writing_stubs.html
[5] https://mypy.readthedocs.io/en/stable/stubgen.html
[6] python/mypy#3217

@juergbijuergbi left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Ideally, we'd have a small script to automate the generation of type.pyi and then we could run that in CI to verify that it's up-to-date. However, I think regeneration is reasonably simple as is and there shouldn't be a functional risk with an outdated type.pyi, so I think it's fine to merge this as is.

@juergbi
juergbi merged commit ad6b437 into apache:masterJul 28, 2026
18 checks passed
return cls._value_to_entry[value]
except KeyError:
if type(value) is cls: # pylint: disable=unidiomatic-typecheck
if isinstance(value, cls): # pylint: disable=unidiomatic-typecheck

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AFAICT your are now doing this right.

https://pylint.pycqa.org/en/latest/user_guide/messages/convention/unidiomatic-typecheck.html

Suggested change
ifisinstance(value, cls):# pylint: disable=unidiomatic-typecheck
ifisinstance(value, cls):

So you dont need to disable the lint.

Comment threadsrc/buildstream/_pipeline.py
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@nathanwilliams-ct@juergbi@wsalmonct
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Static Type Checking: Enums - #2153

Merged
juergbi merged 2 commits into
apache:masterfrom
nathanwilliams-ct:nathan/enum-static-type-checking
Jul 28, 2026
Merged

Static Type Checking: Enums#2153
juergbi merged 2 commits into
apache:masterfrom
nathanwilliams-ct:nathan/enum-static-type-checking

Conversation

@nathanwilliams-ct

Copy link
Copy Markdown
Contributor

Buildstream implements a custom FastEnum[1].
mypy only supports enum.Enum (and it's official variations) when it comes to doing static type checks on enums [2].
We can't subclass Enum in FastEnum to make mypy happy, because Enum doesn't allow subclassing [3].
So we end up with a bunch of str, int etc around the codebase,
instead of the appropriate Enum classes like OverlapAction or _Scope which are usually recorded separately in the doc strings.
This means we don't get proper static type checking on our Enums :( .
With stub files [4][5] we can lie to mypy about what type the Enum classes are[6],
so the type hints around the codebase are now correct be corrected.
Now we can have proper static type checking on the custom Enums, Yay!

So

class OverlapAction(FastEnum):
ERROR: str
WARNING: str
IGNORE: str

gets stubbed as

from enum import Enum
class OverlapAction(Enum):
ERROR: str
WARNING: str
IGNORE: str

[1] src/buildstream/types.py#L32
[2] https://mypy.readthedocs.io/en/stable/literal_types.html#enums
[3] https://docs.python.org/3/howto/enum.html#restricted-enum-subclassing
[4] https://typing.python.org/en/latest/guides/writing_stubs.html
[5] https://mypy.readthedocs.io/en/stable/stubgen.html
[6] python/mypy#3217

Buildstream implements a custom `FastEnum`[1].
mypy only supports `enum.Enum` (and it's official variations) when it comes to doing static type checks on enums [2].
We can't subclass Enum in FastEnum to make mypy happy, because Enum doesn't allow subclassing [3].
So we end up with a bunch of `str`, `int` etc around the codebase,
instead of the appropriate Enum classes like `OverlapAction` or `_Scope` which are usually recorded separately in the doc strings.
This means we don't get proper static type checking on our Enums :( .
With stub files [4][5] we can lie to mypy about what type the Enum classes are[6],
so the type hints around the codebase are now correct be corrected.
Now we can have proper static type checking on the custom Enums, Yay!
So
```
class OverlapAction(FastEnum):
ERROR: str
WARNING: str
IGNORE: str
```
gets stubbed as
```
from enum import Enum
class OverlapAction(Enum):
ERROR: str
WARNING: str
IGNORE: str
```
[1] src/buildstream/types.py#L32
[2] https://mypy.readthedocs.io/en/stable/literal_types.html#enums
[3] https://docs.python.org/3/howto/enum.html#restricted-enum-subclassing
[4] https://typing.python.org/en/latest/guides/writing_stubs.html
[5] https://mypy.readthedocs.io/en/stable/stubgen.html
[6] python/mypy#3217

@juergbijuergbi left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Ideally, we'd have a small script to automate the generation of type.pyi and then we could run that in CI to verify that it's up-to-date. However, I think regeneration is reasonably simple as is and there shouldn't be a functional risk with an outdated type.pyi, so I think it's fine to merge this as is.

@juergbi
juergbi merged commit ad6b437 into apache:masterJul 28, 2026
18 checks passed
return cls._value_to_entry[value]
except KeyError:
if type(value) is cls: # pylint: disable=unidiomatic-typecheck
if isinstance(value, cls): # pylint: disable=unidiomatic-typecheck

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AFAICT your are now doing this right.

https://pylint.pycqa.org/en/latest/user_guide/messages/convention/unidiomatic-typecheck.html

Suggested change
ifisinstance(value, cls):# pylint: disable=unidiomatic-typecheck
ifisinstance(value, cls):

So you dont need to disable the lint.

Comment threadsrc/buildstream/_pipeline.py
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@nathanwilliams-ct@juergbi@wsalmonct
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Static Type Checking: Enums - #2153

Merged
juergbi merged 2 commits into
apache:masterfrom
nathanwilliams-ct:nathan/enum-static-type-checking
Jul 28, 2026
Merged

Static Type Checking: Enums#2153
juergbi merged 2 commits into
apache:masterfrom
nathanwilliams-ct:nathan/enum-static-type-checking

Conversation

@nathanwilliams-ct

Copy link
Copy Markdown
Contributor

Buildstream implements a custom FastEnum[1].
mypy only supports enum.Enum (and it's official variations) when it comes to doing static type checks on enums [2].
We can't subclass Enum in FastEnum to make mypy happy, because Enum doesn't allow subclassing [3].
So we end up with a bunch of str, int etc around the codebase,
instead of the appropriate Enum classes like OverlapAction or _Scope which are usually recorded separately in the doc strings.
This means we don't get proper static type checking on our Enums :( .
With stub files [4][5] we can lie to mypy about what type the Enum classes are[6],
so the type hints around the codebase are now correct be corrected.
Now we can have proper static type checking on the custom Enums, Yay!

So

class OverlapAction(FastEnum):
ERROR: str
WARNING: str
IGNORE: str

gets stubbed as

from enum import Enum
class OverlapAction(Enum):
ERROR: str
WARNING: str
IGNORE: str

[1] src/buildstream/types.py#L32
[2] https://mypy.readthedocs.io/en/stable/literal_types.html#enums
[3] https://docs.python.org/3/howto/enum.html#restricted-enum-subclassing
[4] https://typing.python.org/en/latest/guides/writing_stubs.html
[5] https://mypy.readthedocs.io/en/stable/stubgen.html
[6] python/mypy#3217

Buildstream implements a custom `FastEnum`[1].
mypy only supports `enum.Enum` (and it's official variations) when it comes to doing static type checks on enums [2].
We can't subclass Enum in FastEnum to make mypy happy, because Enum doesn't allow subclassing [3].
So we end up with a bunch of `str`, `int` etc around the codebase,
instead of the appropriate Enum classes like `OverlapAction` or `_Scope` which are usually recorded separately in the doc strings.
This means we don't get proper static type checking on our Enums :( .
With stub files [4][5] we can lie to mypy about what type the Enum classes are[6],
so the type hints around the codebase are now correct be corrected.
Now we can have proper static type checking on the custom Enums, Yay!
So
```
class OverlapAction(FastEnum):
ERROR: str
WARNING: str
IGNORE: str
```
gets stubbed as
```
from enum import Enum
class OverlapAction(Enum):
ERROR: str
WARNING: str
IGNORE: str
```
[1] src/buildstream/types.py#L32
[2] https://mypy.readthedocs.io/en/stable/literal_types.html#enums
[3] https://docs.python.org/3/howto/enum.html#restricted-enum-subclassing
[4] https://typing.python.org/en/latest/guides/writing_stubs.html
[5] https://mypy.readthedocs.io/en/stable/stubgen.html
[6] python/mypy#3217

@juergbijuergbi left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Ideally, we'd have a small script to automate the generation of type.pyi and then we could run that in CI to verify that it's up-to-date. However, I think regeneration is reasonably simple as is and there shouldn't be a functional risk with an outdated type.pyi, so I think it's fine to merge this as is.

@juergbi
juergbi merged commit ad6b437 into apache:masterJul 28, 2026
18 checks passed
return cls._value_to_entry[value]
except KeyError:
if type(value) is cls: # pylint: disable=unidiomatic-typecheck
if isinstance(value, cls): # pylint: disable=unidiomatic-typecheck

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AFAICT your are now doing this right.

https://pylint.pycqa.org/en/latest/user_guide/messages/convention/unidiomatic-typecheck.html

Suggested change
ifisinstance(value, cls):# pylint: disable=unidiomatic-typecheck
ifisinstance(value, cls):

So you dont need to disable the lint.

Comment threadsrc/buildstream/_pipeline.py
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@nathanwilliams-ct@juergbi@wsalmonct
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Static Type Checking: Enums - #2153

Merged
juergbi merged 2 commits into
apache:masterfrom
nathanwilliams-ct:nathan/enum-static-type-checking
Jul 28, 2026
Merged

Static Type Checking: Enums#2153
juergbi merged 2 commits into
apache:masterfrom
nathanwilliams-ct:nathan/enum-static-type-checking

Conversation

@nathanwilliams-ct

Copy link
Copy Markdown
Contributor

Buildstream implements a custom FastEnum[1].
mypy only supports enum.Enum (and it's official variations) when it comes to doing static type checks on enums [2].
We can't subclass Enum in FastEnum to make mypy happy, because Enum doesn't allow subclassing [3].
So we end up with a bunch of str, int etc around the codebase,
instead of the appropriate Enum classes like OverlapAction or _Scope which are usually recorded separately in the doc strings.
This means we don't get proper static type checking on our Enums :( .
With stub files [4][5] we can lie to mypy about what type the Enum classes are[6],
so the type hints around the codebase are now correct be corrected.
Now we can have proper static type checking on the custom Enums, Yay!

So

class OverlapAction(FastEnum):
ERROR: str
WARNING: str
IGNORE: str

gets stubbed as

from enum import Enum
class OverlapAction(Enum):
ERROR: str
WARNING: str
IGNORE: str

[1] src/buildstream/types.py#L32
[2] https://mypy.readthedocs.io/en/stable/literal_types.html#enums
[3] https://docs.python.org/3/howto/enum.html#restricted-enum-subclassing
[4] https://typing.python.org/en/latest/guides/writing_stubs.html
[5] https://mypy.readthedocs.io/en/stable/stubgen.html
[6] python/mypy#3217

Buildstream implements a custom `FastEnum`[1].
mypy only supports `enum.Enum` (and it's official variations) when it comes to doing static type checks on enums [2].
We can't subclass Enum in FastEnum to make mypy happy, because Enum doesn't allow subclassing [3].
So we end up with a bunch of `str`, `int` etc around the codebase,
instead of the appropriate Enum classes like `OverlapAction` or `_Scope` which are usually recorded separately in the doc strings.
This means we don't get proper static type checking on our Enums :( .
With stub files [4][5] we can lie to mypy about what type the Enum classes are[6],
so the type hints around the codebase are now correct be corrected.
Now we can have proper static type checking on the custom Enums, Yay!
So
```
class OverlapAction(FastEnum):
ERROR: str
WARNING: str
IGNORE: str
```
gets stubbed as
```
from enum import Enum
class OverlapAction(Enum):
ERROR: str
WARNING: str
IGNORE: str
```
[1] src/buildstream/types.py#L32
[2] https://mypy.readthedocs.io/en/stable/literal_types.html#enums
[3] https://docs.python.org/3/howto/enum.html#restricted-enum-subclassing
[4] https://typing.python.org/en/latest/guides/writing_stubs.html
[5] https://mypy.readthedocs.io/en/stable/stubgen.html
[6] python/mypy#3217

@juergbijuergbi left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Ideally, we'd have a small script to automate the generation of type.pyi and then we could run that in CI to verify that it's up-to-date. However, I think regeneration is reasonably simple as is and there shouldn't be a functional risk with an outdated type.pyi, so I think it's fine to merge this as is.

@juergbi
juergbi merged commit ad6b437 into apache:masterJul 28, 2026
18 checks passed
return cls._value_to_entry[value]
except KeyError:
if type(value) is cls: # pylint: disable=unidiomatic-typecheck
if isinstance(value, cls): # pylint: disable=unidiomatic-typecheck

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AFAICT your are now doing this right.

https://pylint.pycqa.org/en/latest/user_guide/messages/convention/unidiomatic-typecheck.html

Suggested change
ifisinstance(value, cls):# pylint: disable=unidiomatic-typecheck
ifisinstance(value, cls):

So you dont need to disable the lint.

Comment threadsrc/buildstream/_pipeline.py
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@nathanwilliams-ct@juergbi@wsalmonct
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Static Type Checking: Enums - #2153

Merged
juergbi merged 2 commits into
apache:masterfrom
nathanwilliams-ct:nathan/enum-static-type-checking
Jul 28, 2026
Merged

Static Type Checking: Enums#2153
juergbi merged 2 commits into
apache:masterfrom
nathanwilliams-ct:nathan/enum-static-type-checking

Conversation

@nathanwilliams-ct

Copy link
Copy Markdown
Contributor

Buildstream implements a custom FastEnum[1].
mypy only supports enum.Enum (and it's official variations) when it comes to doing static type checks on enums [2].
We can't subclass Enum in FastEnum to make mypy happy, because Enum doesn't allow subclassing [3].
So we end up with a bunch of str, int etc around the codebase,
instead of the appropriate Enum classes like OverlapAction or _Scope which are usually recorded separately in the doc strings.
This means we don't get proper static type checking on our Enums :( .
With stub files [4][5] we can lie to mypy about what type the Enum classes are[6],
so the type hints around the codebase are now correct be corrected.
Now we can have proper static type checking on the custom Enums, Yay!

So

class OverlapAction(FastEnum):
ERROR: str
WARNING: str
IGNORE: str

gets stubbed as

from enum import Enum
class OverlapAction(Enum):
ERROR: str
WARNING: str
IGNORE: str

[1] src/buildstream/types.py#L32
[2] https://mypy.readthedocs.io/en/stable/literal_types.html#enums
[3] https://docs.python.org/3/howto/enum.html#restricted-enum-subclassing
[4] https://typing.python.org/en/latest/guides/writing_stubs.html
[5] https://mypy.readthedocs.io/en/stable/stubgen.html
[6] python/mypy#3217

Buildstream implements a custom `FastEnum`[1].
mypy only supports `enum.Enum` (and it's official variations) when it comes to doing static type checks on enums [2].
We can't subclass Enum in FastEnum to make mypy happy, because Enum doesn't allow subclassing [3].
So we end up with a bunch of `str`, `int` etc around the codebase,
instead of the appropriate Enum classes like `OverlapAction` or `_Scope` which are usually recorded separately in the doc strings.
This means we don't get proper static type checking on our Enums :( .
With stub files [4][5] we can lie to mypy about what type the Enum classes are[6],
so the type hints around the codebase are now correct be corrected.
Now we can have proper static type checking on the custom Enums, Yay!
So
```
class OverlapAction(FastEnum):
ERROR: str
WARNING: str
IGNORE: str
```
gets stubbed as
```
from enum import Enum
class OverlapAction(Enum):
ERROR: str
WARNING: str
IGNORE: str
```
[1] src/buildstream/types.py#L32
[2] https://mypy.readthedocs.io/en/stable/literal_types.html#enums
[3] https://docs.python.org/3/howto/enum.html#restricted-enum-subclassing
[4] https://typing.python.org/en/latest/guides/writing_stubs.html
[5] https://mypy.readthedocs.io/en/stable/stubgen.html
[6] python/mypy#3217

@juergbijuergbi left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Ideally, we'd have a small script to automate the generation of type.pyi and then we could run that in CI to verify that it's up-to-date. However, I think regeneration is reasonably simple as is and there shouldn't be a functional risk with an outdated type.pyi, so I think it's fine to merge this as is.

@juergbi
juergbi merged commit ad6b437 into apache:masterJul 28, 2026
18 checks passed
return cls._value_to_entry[value]
except KeyError:
if type(value) is cls: # pylint: disable=unidiomatic-typecheck
if isinstance(value, cls): # pylint: disable=unidiomatic-typecheck

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AFAICT your are now doing this right.

https://pylint.pycqa.org/en/latest/user_guide/messages/convention/unidiomatic-typecheck.html

Suggested change
ifisinstance(value, cls):# pylint: disable=unidiomatic-typecheck
ifisinstance(value, cls):

So you dont need to disable the lint.

Comment threadsrc/buildstream/_pipeline.py
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@nathanwilliams-ct@juergbi@wsalmonct
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Static Type Checking: Enums - #2153

Merged
juergbi merged 2 commits into
apache:masterfrom
nathanwilliams-ct:nathan/enum-static-type-checking
Jul 28, 2026
Merged

Static Type Checking: Enums#2153
juergbi merged 2 commits into
apache:masterfrom
nathanwilliams-ct:nathan/enum-static-type-checking

Conversation

@nathanwilliams-ct

Copy link
Copy Markdown
Contributor

Buildstream implements a custom FastEnum[1].
mypy only supports enum.Enum (and it's official variations) when it comes to doing static type checks on enums [2].
We can't subclass Enum in FastEnum to make mypy happy, because Enum doesn't allow subclassing [3].
So we end up with a bunch of str, int etc around the codebase,
instead of the appropriate Enum classes like OverlapAction or _Scope which are usually recorded separately in the doc strings.
This means we don't get proper static type checking on our Enums :( .
With stub files [4][5] we can lie to mypy about what type the Enum classes are[6],
so the type hints around the codebase are now correct be corrected.
Now we can have proper static type checking on the custom Enums, Yay!

So

class OverlapAction(FastEnum):
ERROR: str
WARNING: str
IGNORE: str

gets stubbed as

from enum import Enum
class OverlapAction(Enum):
ERROR: str
WARNING: str
IGNORE: str

[1] src/buildstream/types.py#L32
[2] https://mypy.readthedocs.io/en/stable/literal_types.html#enums
[3] https://docs.python.org/3/howto/enum.html#restricted-enum-subclassing
[4] https://typing.python.org/en/latest/guides/writing_stubs.html
[5] https://mypy.readthedocs.io/en/stable/stubgen.html
[6] python/mypy#3217

Buildstream implements a custom `FastEnum`[1].
mypy only supports `enum.Enum` (and it's official variations) when it comes to doing static type checks on enums [2].
We can't subclass Enum in FastEnum to make mypy happy, because Enum doesn't allow subclassing [3].
So we end up with a bunch of `str`, `int` etc around the codebase,
instead of the appropriate Enum classes like `OverlapAction` or `_Scope` which are usually recorded separately in the doc strings.
This means we don't get proper static type checking on our Enums :( .
With stub files [4][5] we can lie to mypy about what type the Enum classes are[6],
so the type hints around the codebase are now correct be corrected.
Now we can have proper static type checking on the custom Enums, Yay!
So
```
class OverlapAction(FastEnum):
ERROR: str
WARNING: str
IGNORE: str
```
gets stubbed as
```
from enum import Enum
class OverlapAction(Enum):
ERROR: str
WARNING: str
IGNORE: str
```
[1] src/buildstream/types.py#L32
[2] https://mypy.readthedocs.io/en/stable/literal_types.html#enums
[3] https://docs.python.org/3/howto/enum.html#restricted-enum-subclassing
[4] https://typing.python.org/en/latest/guides/writing_stubs.html
[5] https://mypy.readthedocs.io/en/stable/stubgen.html
[6] python/mypy#3217

@juergbijuergbi left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Ideally, we'd have a small script to automate the generation of type.pyi and then we could run that in CI to verify that it's up-to-date. However, I think regeneration is reasonably simple as is and there shouldn't be a functional risk with an outdated type.pyi, so I think it's fine to merge this as is.

@juergbi
juergbi merged commit ad6b437 into apache:masterJul 28, 2026
18 checks passed
return cls._value_to_entry[value]
except KeyError:
if type(value) is cls: # pylint: disable=unidiomatic-typecheck
if isinstance(value, cls): # pylint: disable=unidiomatic-typecheck

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AFAICT your are now doing this right.

https://pylint.pycqa.org/en/latest/user_guide/messages/convention/unidiomatic-typecheck.html

Suggested change
ifisinstance(value, cls):# pylint: disable=unidiomatic-typecheck
ifisinstance(value, cls):

So you dont need to disable the lint.

Comment threadsrc/buildstream/_pipeline.py
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@nathanwilliams-ct@juergbi@wsalmonct
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Static Type Checking: Enums - #2153

Merged
juergbi merged 2 commits into
apache:masterfrom
nathanwilliams-ct:nathan/enum-static-type-checking
Jul 28, 2026
Merged

Static Type Checking: Enums#2153
juergbi merged 2 commits into
apache:masterfrom
nathanwilliams-ct:nathan/enum-static-type-checking

Conversation

@nathanwilliams-ct

Copy link
Copy Markdown
Contributor

Buildstream implements a custom FastEnum[1].
mypy only supports enum.Enum (and it's official variations) when it comes to doing static type checks on enums [2].
We can't subclass Enum in FastEnum to make mypy happy, because Enum doesn't allow subclassing [3].
So we end up with a bunch of str, int etc around the codebase,
instead of the appropriate Enum classes like OverlapAction or _Scope which are usually recorded separately in the doc strings.
This means we don't get proper static type checking on our Enums :( .
With stub files [4][5] we can lie to mypy about what type the Enum classes are[6],
so the type hints around the codebase are now correct be corrected.
Now we can have proper static type checking on the custom Enums, Yay!

So

class OverlapAction(FastEnum):
ERROR: str
WARNING: str
IGNORE: str

gets stubbed as

from enum import Enum
class OverlapAction(Enum):
ERROR: str
WARNING: str
IGNORE: str

[1] src/buildstream/types.py#L32
[2] https://mypy.readthedocs.io/en/stable/literal_types.html#enums
[3] https://docs.python.org/3/howto/enum.html#restricted-enum-subclassing
[4] https://typing.python.org/en/latest/guides/writing_stubs.html
[5] https://mypy.readthedocs.io/en/stable/stubgen.html
[6] python/mypy#3217

Buildstream implements a custom `FastEnum`[1].
mypy only supports `enum.Enum` (and it's official variations) when it comes to doing static type checks on enums [2].
We can't subclass Enum in FastEnum to make mypy happy, because Enum doesn't allow subclassing [3].
So we end up with a bunch of `str`, `int` etc around the codebase,
instead of the appropriate Enum classes like `OverlapAction` or `_Scope` which are usually recorded separately in the doc strings.
This means we don't get proper static type checking on our Enums :( .
With stub files [4][5] we can lie to mypy about what type the Enum classes are[6],
so the type hints around the codebase are now correct be corrected.
Now we can have proper static type checking on the custom Enums, Yay!
So
```
class OverlapAction(FastEnum):
ERROR: str
WARNING: str
IGNORE: str
```
gets stubbed as
```
from enum import Enum
class OverlapAction(Enum):
ERROR: str
WARNING: str
IGNORE: str
```
[1] src/buildstream/types.py#L32
[2] https://mypy.readthedocs.io/en/stable/literal_types.html#enums
[3] https://docs.python.org/3/howto/enum.html#restricted-enum-subclassing
[4] https://typing.python.org/en/latest/guides/writing_stubs.html
[5] https://mypy.readthedocs.io/en/stable/stubgen.html
[6] python/mypy#3217

@juergbijuergbi left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Ideally, we'd have a small script to automate the generation of type.pyi and then we could run that in CI to verify that it's up-to-date. However, I think regeneration is reasonably simple as is and there shouldn't be a functional risk with an outdated type.pyi, so I think it's fine to merge this as is.

@juergbi
juergbi merged commit ad6b437 into apache:masterJul 28, 2026
18 checks passed
return cls._value_to_entry[value]
except KeyError:
if type(value) is cls: # pylint: disable=unidiomatic-typecheck
if isinstance(value, cls): # pylint: disable=unidiomatic-typecheck

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AFAICT your are now doing this right.

https://pylint.pycqa.org/en/latest/user_guide/messages/convention/unidiomatic-typecheck.html

Suggested change
ifisinstance(value, cls):# pylint: disable=unidiomatic-typecheck
ifisinstance(value, cls):

So you dont need to disable the lint.

Comment threadsrc/buildstream/_pipeline.py
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@nathanwilliams-ct@juergbi@wsalmonct