FazBrowse GitHub Viewer
|
Trending
|
URL:
|
Home
Tools:
[Download Repo ZIP]
[View Raw Code]
[Original HTTPS Page]
pythonnative/scripts/generate-icons.py at main · pythonnative/pythonnative · GitHub
pythonnative
/
pythonnative
Public
Notifications
You must be signed in to change notification settings
Fork
23
Star
131
Code
Issues
11
Pull requests
4
Actions
Projects
Security and quality
0
Insights
Additional navigation options
Code
Issues
Pull requests
Actions
Projects
Security and quality
Insights
Expand file tree
Breadcrumbs
pythonnative
/
scripts
/
generate-icons.py
Copy path
More file actions
More file actions
Latest commit
History
History
History
162 lines (138 loc) · 6.4 KB
Breadcrumbs
pythonnative
/
scripts
/
generate-icons.py
Copy path
File metadata and controls
162 lines (138 loc) · 6.4 KB
Raw
Copy raw file
Download raw file
Open symbols panel
Edit and raw actions
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
#!/usr/bin/env python3
"""Regenerate the bundled Lucide icon data and the ``IconName`` literal.
Usage:
python scripts/generate-icons.py --tag 1.47.0
python scripts/generate-icons.py --source /path/to/lucide/icons --version 1.47.0
With ``--tag`` the script downloads the release archive from GitHub into a
temporary directory. With ``--source`` it reads an already extracted
``icons/`` directory (the folder holding ``heart.svg``, ``heart.json``,
and so on).
Outputs (both checked in):
- ``src/pythonnative/icons/_lucide.json``: ``{"version", "license",
"icons": {name: [shape, ...]}}`` where each shape is either a path
string or a small dict for the other SVG basic shapes, all in the
24 by 24 Lucide grid.
- ``src/pythonnative/icons/_names.py``: the ``IconName`` ``Literal`` and
``ICON_NAMES`` tuple used for editor completion and validation.
Lucide is ISC licensed; the license text is copied into
``src/pythonnative/icons/LICENSE-lucide.txt``.
"""
from
__future__
import
annotations
import
argparse
import
io
import
json
import
re
import
sys
import
tarfile
import
tempfile
import
urllib
.
request
import
xml
.
etree
.
ElementTree
as
ET
from
pathlib
import
Path
from
typing
import
Any
,
Dict
,
List
ROOT
=
Path
(
__file__
).
resolve
().
parents
[
1
]
OUT_DIR
=
ROOT
/
"src"
/
"pythonnative"
/
"icons"
SVG_NS
=
"{http://www.w3.org/2000/svg}"
NUMERIC
=
(
"cx"
,
"cy"
,
"r"
,
"rx"
,
"ry"
,
"x"
,
"y"
,
"width"
,
"height"
,
"x1"
,
"y1"
,
"x2"
,
"y2"
)
def
_number
(
text
:
str
)
->
float
:
value
=
float
(
text
)
return
int
(
value
)
if
value
.
is_integer
()
else
value
def
convert
(
svg_text
:
str
)
->
List
[
Any
]:
"""Return the shape list for one Lucide SVG document."""
root
=
ET
.
fromstring
(
svg_text
)
shapes
:
List
[
Any
]
=
[]
for
element
in
root
:
tag
=
element
.
tag
.
replace
(
SVG_NS
,
""
)
if
tag
==
"path"
:
d
=
element
.
attrib
.
get
(
"d"
,
""
).
strip
()
if
d
:
shapes
.
append
(
re
.
sub
(
r"\s+"
,
" "
,
d
))
elif
tag
in
(
"circle"
,
"ellipse"
,
"rect"
,
"line"
):
shape
:
Dict
[
str
,
Any
]
=
{
"kind"
:
tag
}
for
key
in
NUMERIC
:
if
key
in
element
.
attrib
:
shape
[
key
]
=
_number
(
element
.
attrib
[
key
])
shapes
.
append
(
shape
)
elif
tag
in
(
"polyline"
,
"polygon"
):
shapes
.
append
({
"kind"
:
tag
,
"points"
:
re
.
sub
(
r"\s+"
,
" "
,
element
.
attrib
.
get
(
"points"
,
""
).
strip
())})
else
:
raise
ValueError
(
f"unsupported element <
{
tag
}
>"
)
return
shapes
def
load_icons
(
source
:
Path
)
->
Dict
[
str
,
List
[
Any
]]:
"""Read every ``*.svg`` under ``source`` into a name -> shapes mapping."""
icons
:
Dict
[
str
,
List
[
Any
]]
=
{}
for
path
in
sorted
(
source
.
glob
(
"*.svg"
)):
name
=
path
.
stem
if
not
re
.
fullmatch
(
r"[a-z0-9]+(?:-[a-z0-9]+)*"
,
name
):
print
(
f"skipping oddly named icon
{
name
!r
}
"
,
file
=
sys
.
stderr
)
continue
icons
[
name
]
=
convert
(
path
.
read_text
(
encoding
=
"utf-8"
))
if
not
icons
:
raise
SystemExit
(
f"no icons found under
{
source
}
"
)
return
icons
def
download
(
tag
:
str
,
destination
:
Path
)
->
tuple
[
Path
,
str
]:
"""Fetch the Lucide release archive for ``tag``; return (icons dir, license)."""
url
=
f"https://github.com/lucide-icons/lucide/archive/refs/tags/
{
tag
}
.tar.gz"
print
(
f"downloading
{
url
}
"
,
file
=
sys
.
stderr
)
with
urllib
.
request
.
urlopen
(
url
)
as
response
:
# noqa: S310 - fixed GitHub host
data
=
response
.
read
()
with
tarfile
.
open
(
fileobj
=
io
.
BytesIO
(
data
),
mode
=
"r:gz"
)
as
archive
:
members
=
[
m
for
m
in
archive
.
getmembers
()
if
"/icons/"
in
m
.
name
or
m
.
name
.
endswith
(
"/LICENSE"
)]
archive
.
extractall
(
destination
,
members
=
members
)
# noqa: S202 - trusted release archive
top
=
next
(
destination
.
iterdir
())
return
top
/
"icons"
, (
top
/
"LICENSE"
).
read_text
(
encoding
=
"utf-8"
)
def
write_outputs
(
icons
:
Dict
[
str
,
List
[
Any
]],
version
:
str
,
license_text
:
str
|
None
)
->
None
:
"""Write the JSON data, the names module, and the license."""
OUT_DIR
.
mkdir
(
parents
=
True
,
exist_ok
=
True
)
payload
=
{
"version"
:
version
,
"license"
:
"ISC"
,
"source"
:
"https://lucide.dev"
,
"icons"
:
icons
}
(
OUT_DIR
/
"_lucide.json"
).
write_text
(
json
.
dumps
(
payload
,
separators
=
(
","
,
":"
),
sort_keys
=
True
,
ensure_ascii
=
True
)
+
"
\n
"
,
encoding
=
"utf-8"
)
names
=
sorted
(
icons
)
lines
=
[
'"""Generated by scripts/generate-icons.py. Do not edit by hand."""'
,
""
,
"# ruff: noqa: E501"
,
"# fmt: off"
,
"from typing import Literal, Tuple"
,
""
,
f'LUCIDE_VERSION = "
{
version
}
"'
,
""
,
"IconName = Literal["
,
*
(
f' "
{
name
}
",'
for
name
in
names
),
"]"
,
'"""Every icon name bundled with PythonNative (Lucide).'
,
""
,
"Used for editor completion and type checking of ``pn.Icon(name)``."
,
'"""'
,
""
,
"ICON_NAMES: Tuple[str, ...] = ("
,
*
(
f' "
{
name
}
",'
for
name
in
names
),
")"
,
""
,
]
(
OUT_DIR
/
"_names.py"
).
write_text
(
"
\n
"
.
join
(
lines
),
encoding
=
"utf-8"
)
if
license_text
:
(
OUT_DIR
/
"LICENSE-lucide.txt"
).
write_text
(
license_text
,
encoding
=
"utf-8"
)
print
(
f"wrote
{
len
(
names
)
}
icons (Lucide
{
version
}
) to
{
OUT_DIR
}
"
,
file
=
sys
.
stderr
)
def
main
(
argv
:
List
[
str
])
->
int
:
"""Download the icon set and regenerate the packaged data files."""
parser
=
argparse
.
ArgumentParser
(
description
=
__doc__
,
formatter_class
=
argparse
.
RawDescriptionHelpFormatter
)
parser
.
add_argument
(
"--tag"
,
help
=
"Lucide release tag to download (e.g. 1.47.0)"
)
parser
.
add_argument
(
"--source"
,
type
=
Path
,
help
=
"Extracted Lucide icons/ directory"
)
parser
.
add_argument
(
"--version"
,
help
=
"Version string recorded in the output (defaults to --tag)"
)
parser
.
add_argument
(
"--license"
,
type
=
Path
,
help
=
"Path to the Lucide LICENSE file when using --source"
)
args
=
parser
.
parse_args
(
argv
)
if
not
args
.
tag
and
not
args
.
source
:
parser
.
error
(
"one of --tag or --source is required"
)
if
args
.
source
:
icons
=
load_icons
(
args
.
source
)
version
=
args
.
version
or
args
.
tag
or
"unknown"
license_text
=
args
.
license
.
read_text
(
encoding
=
"utf-8"
)
if
args
.
license
else
None
write_outputs
(
icons
,
version
,
license_text
)
return
0
with
tempfile
.
TemporaryDirectory
(
prefix
=
"lucide-"
)
as
folder
:
icons_dir
,
license_text
=
download
(
args
.
tag
,
Path
(
folder
))
write_outputs
(
load_icons
(
icons_dir
),
args
.
version
or
args
.
tag
,
license_text
)
return
0
if
__name__
==
"__main__"
:
sys
.
exit
(
main
(
sys
.
argv
[
1
:]))
Back
|
FazBrowse Home
|
New Git URL