Fetch the repository succeeded.
#!/usr/bin/env python3
import re
from dataclasses import dataclass, field
from pathlib import Path
from typing import List
import requests
from bs4 import BeautifulSoup
@dataclass
class Property:
title: str
description: str
members: List[str]
short_members: List[str] = field(init=False)
common_prefix: str = field(init=False)
def __post_init__(self) -> None:
words = [s.split('-') for s in self.members]
prefix = words[0]
for w in words:
i = 0
while i < len(prefix) and i < len(w) and prefix[i] == w[i]:
i += 1
prefix = prefix[:i]
if not prefix:
break
self.short_members = ['-'.join(word[len(prefix):]) for word in words]
self.common_prefix = '-'.join(prefix) + '-' if prefix else ''
if len(self.short_members) == 1:
if self.title == 'Container':
self.members.clear()
self.short_members.clear()
self.common_prefix = 'container'
elif self.title in {'List Style Image', 'Content', 'Appearance'}:
self.short_members = ['none']
self.common_prefix = self.members[0].removesuffix('-none')
else:
raise ValueError(f'Unknown single-value property "{self.title}"')
@property
def pascal_title(self) -> str:
return ''.join(word.capitalize() for word in re.sub(r'[-/ &]', ' ', self.title).split())
@property
def snake_title(self) -> str:
return '_'.join(word.lower() for word in re.sub(r'[-/ &]', ' ', self.title).split())
def get_soup(url: str) -> BeautifulSoup:
"""Get the BeautifulSoup object for the given URL."""
path = Path('/tmp/nicegui_tailwind') / url.split('/')[-1]
path.parent.mkdir(parents=True, exist_ok=True)
if path.exists():
html = path.read_text()
else:
req = requests.get(url, timeout=5)
html = req.text
path.write_text(html)
return BeautifulSoup(html, 'html.parser')
def collect_properties() -> List[Property]:
"""Collect all Tailwind properties from the documentation."""
properties: List[Property] = []
soup = get_soup('https://tailwindcss.com/docs')
for li in soup.select('li[class="mt-12 lg:mt-8"]'):
title = li.select_one('h5').text
links = li.select('li a')
if title in {'Getting Started', 'Core Concepts', 'Customization', 'Base Styles', 'Official Plugins'}:
continue
print(f'{title}:')
for a in links:
soup = get_soup(f'https://tailwindcss.com{a["href"]}')
title = soup.select_one('#header h1').text
description = soup.select_one('#header .mt-2').text
members = soup.select('.mt-10 td[class*=text-sky-400]')
properties.append(Property(title, description, [p.text.split(' ')[0] for p in members]))
print(f'\t{title} ({len(members)})')
return properties
def generate_type_files(properties: List[Property]) -> None:
"""Generate the type files for the Tailwind properties."""
for file in (Path(__file__).parent / 'nicegui' / 'tailwind_types').glob('*.py'):
file.unlink()
(Path(__file__).parent / 'nicegui' / 'tailwind_types' / '__init__.py').touch()
for property_ in properties:
if not property_.members:
continue
with (Path(__file__).parent / 'nicegui' / 'tailwind_types' / f'{property_.snake_title}.py').open('w') as f:
f.write('from typing import Literal\n')
f.write('\n')
f.write(f'{property_.pascal_title} = Literal[\n')
for short_member in property_.short_members:
f.write(f" '{short_member}',\n")
f.write(']\n')
def generate_tailwind_file(properties: List[Property]) -> None:
"""Generate the tailwind.py file."""
with (Path(__file__).parent / 'nicegui' / 'tailwind.py').open('w') as f:
f.write('# pylint: disable=too-many-lines\n')
f.write('from __future__ import annotations\n')
f.write('\n')
f.write('from typing import TYPE_CHECKING, List, Optional, Union, overload\n')
f.write('\n')
f.write('if TYPE_CHECKING:\n')
f.write(' from .element import Element\n')
for property_ in sorted(properties, key=lambda p: p.title):
if not property_.members:
continue
f.write(f' from .tailwind_types.{property_.snake_title} import {property_.pascal_title}\n')
f.write('\n')
f.write('\n')
f.write('class PseudoElement:\n')
f.write('\n')
f.write(' def __init__(self) -> None:\n')
f.write(' self._classes: List[str] = []\n')
f.write('\n')
f.write(' def classes(self, add: str) -> None:\n')
f.write(' """Add the given classes to the element."""\n')
f.write(' self._classes.append(add)\n')
f.write('\n')
f.write('\n')
f.write('class Tailwind:\n')
f.write('\n')
f.write(' def __init__(self, _element: Optional[Element] = None) -> None:\n')
f.write(
' self.element: Union[PseudoElement, Element] = PseudoElement() if _element is None else _element\n')
f.write('\n')
f.write(' @overload\n')
f.write(' def __call__(self, tailwind: Tailwind) -> Tailwind:\n')
f.write(' ...\n')
f.write('\n')
f.write(' @overload\n')
f.write(' def __call__(self, *classes: str) -> Tailwind:\n')
f.write(' ...\n')
f.write('\n')
f.write(' def __call__(self, *args) -> Tailwind: # type: ignore\n')
f.write(' if not args:\n')
f.write(' return self\n')
f.write(' if isinstance(args[0], Tailwind):\n')
f.write(' args[0].apply(self.element) # type: ignore\n')
f.write(' else:\n')
f.write(" self.element.classes(' '.join(args))\n")
f.write(' return self\n')
f.write('\n')
f.write(' def apply(self, element: Element) -> None:\n')
f.write(' """Apply the tailwind classes to the given element."""\n')
f.write(' element._classes.extend(self.element._classes) # pylint: disable=protected-access\n')
f.write(' element.update()\n')
for property_ in properties:
f.write('\n')
prefix = property_.common_prefix
if property_.members:
f.write(f' def {property_.snake_title}(self, value: {property_.pascal_title}) -> Tailwind:\n')
f.write(f' """{property_.description}"""\n')
if '' in property_.short_members:
f.write(
f" self.element.classes('{prefix}' + value if value else '{prefix.rstrip('''-''')}')\n")
else:
f.write(f" self.element.classes('{prefix}' + value)\n")
f.write(' return self\n')
else:
f.write(f' def {property_.snake_title}(self) -> Tailwind:\n')
f.write(f' """{property_.description}"""\n')
f.write(f" self.element.classes('{prefix}')\n")
f.write(' return self\n')
def main() -> None:
"""Collect all Tailwind properties from the documentation and generate the Python files."""
properties = collect_properties()
generate_type_files(properties)
generate_tailwind_file(properties)
if __name__ == '__main__':
main()
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。