Browse Source

Merge pull request #2317 from RJohnPaul/patch-2

Update fetch_tailwind.py with Refactored Tailwind CSS Integrations
Falko Schindler 1 year ago
parent
commit
1d1f4a15f5
1 changed files with 116 additions and 95 deletions
  1. 116 95
      fetch_tailwind.py

+ 116 - 95
fetch_tailwind.py

@@ -18,7 +18,7 @@ class Property:
 
     def __post_init__(self) -> None:
         words = [s.split('-') for s in self.members]
-        prefix = words[0]  # pylint: disable=redefined-outer-name
+        prefix = words[0]
         for w in words:
             i = 0
             while i < len(prefix) and i < len(w) and prefix[i] == w[i]:
@@ -48,10 +48,8 @@ class Property:
         return '_'.join(word.lower() for word in re.sub(r'[-/ &]', ' ', self.title).split())
 
 
-properties: List[Property] = []
-
-
 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():
@@ -63,98 +61,121 @@ def get_soup(url: str) -> BeautifulSoup:
     return BeautifulSoup(html, 'html.parser')
 
 
-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)})')
-
-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')
-
-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:
+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
-        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')
+        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')
-        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")
+        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(f'        return self\n')  # pylint: disable=f-string-without-interpolation
             else:
-                f.write(f"        self.element.classes('{prefix}' + value)\n")
-            f.write(f'        return self\n')  # pylint: disable=f-string-without-interpolation
-        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(f'        return self\n')  # pylint: disable=f-string-without-interpolation
+                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(f'        return self\n')  # pylint: disable=f-string-without-interpolation
+
+
+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()