Compare commits
28 Commits
Author | SHA1 | Date | |
---|---|---|---|
90e30821eb | |||
b3a4862e46 | |||
763f09051a | |||
eaef972c88 | |||
5fb229c09f | |||
a3773df853 | |||
0f68796831 | |||
deb3460df3 | |||
afa2f0259d | |||
b450fd10fe | |||
dfbb907274 | |||
feeaef6aa4 | |||
9d01997275 | |||
e47bff3204 | |||
701400d442 | |||
26ca1cd012 | |||
7e318b2001 | |||
4dc3a6892a | |||
9a35859210 | |||
cbd484db6a | |||
41d994e8fc | |||
2b8b50d084 | |||
65533b8d99 | |||
9ee5778933 | |||
8d5f60ca72 | |||
6648b607ff | |||
411ad512e5 | |||
28eb68f57a |
3
.gitignore
vendored
3
.gitignore
vendored
@ -1,5 +1,5 @@
|
|||||||
*.pyc
|
*.pyc
|
||||||
__pycache__
|
__pycache__/
|
||||||
|
|
||||||
*.idea
|
*.idea
|
||||||
|
|
||||||
@ -7,6 +7,7 @@ build/
|
|||||||
dist/
|
dist/
|
||||||
*.egg-info/
|
*.egg-info/
|
||||||
.mypy_cache/
|
.mypy_cache/
|
||||||
|
.pytest_cache/
|
||||||
|
|
||||||
*.swp
|
*.swp
|
||||||
*.swo
|
*.swo
|
||||||
|
@ -6,13 +6,13 @@
|
|||||||
### Links
|
### Links
|
||||||
- [Source repository](https://mpxd.net/code/jan/g85)
|
- [Source repository](https://mpxd.net/code/jan/g85)
|
||||||
- [PyPI](https://pypi.org/project/g85)
|
- [PyPI](https://pypi.org/project/g85)
|
||||||
|
- [Github mirror](https://github.com/anewusername/g85)
|
||||||
|
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
Requirements:
|
Requirements:
|
||||||
* python >= 3.7 (written and tested with 3.9)
|
* python >= 3.10 (written and tested with 3.11)
|
||||||
* numpy
|
|
||||||
|
|
||||||
|
|
||||||
Install with pip:
|
Install with pip:
|
||||||
|
1
g85/LICENSE.md
Symbolic link
1
g85/LICENSE.md
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
../LICENSE.md
|
1
g85/README.md
Symbolic link
1
g85/README.md
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
../README.md
|
@ -1,4 +0,0 @@
|
|||||||
""" VERSION defintion. THIS FILE IS MANUALLY PARSED BY setup.py and REQUIRES A SPECIFIC FORMAT """
|
|
||||||
__version__ = '''
|
|
||||||
0.1
|
|
||||||
'''.strip()
|
|
@ -1,3 +1,9 @@
|
|||||||
from .main import Map, Device
|
from .main import (
|
||||||
from .read import read
|
Map as Map,
|
||||||
from .write import write
|
Device as Device,
|
||||||
|
)
|
||||||
|
from .read import read as read
|
||||||
|
from .write import write as write
|
||||||
|
|
||||||
|
__author__ = 'Jan Petykiewicz'
|
||||||
|
__version__ = '0.7'
|
||||||
|
39
g85/main.py
39
g85/main.py
@ -1,4 +1,3 @@
|
|||||||
from typing import Dict, List, Tuple, Union, Optional
|
|
||||||
import datetime
|
import datetime
|
||||||
from collections import Counter
|
from collections import Counter
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
@ -8,25 +7,25 @@ from itertools import chain
|
|||||||
@dataclass
|
@dataclass
|
||||||
class Device:
|
class Device:
|
||||||
BinType: str
|
BinType: str
|
||||||
NullBin: Union[str, int]
|
NullBin: str | int
|
||||||
ProductId: Optional[str] = None
|
ProductId: str | None = None
|
||||||
LotId: Optional[str] = None
|
LotId: str | None = None
|
||||||
WaferSize: Optional[float] = None
|
WaferSize: float | None = None
|
||||||
CreateDate: Optional[datetime.datetime] = None
|
CreateDate: datetime.datetime | None = None
|
||||||
DeviceSizeX: Optional[float] = None
|
DeviceSizeX: float | None = None
|
||||||
DeviceSizeY: Optional[float] = None
|
DeviceSizeY: float | None = None
|
||||||
SupplierName: Optional[str] = None
|
SupplierName: str | None = None
|
||||||
OriginLocation: Optional[int] = None
|
OriginLocation: int | None = None
|
||||||
MapType: str = 'Array'
|
MapType: str = 'Array'
|
||||||
Orientation: float = 0
|
Orientation: float = 0
|
||||||
reference_xy: Optional[Tuple[int, int]] = None
|
reference_xy: tuple[int, int] | None = None
|
||||||
|
|
||||||
bin_pass: Dict[Union[int, str], bool] = field(default_factory=dict) # Is this bin passing?
|
bin_pass: dict[int | str, bool] = field(default_factory=dict) # Is this bin passing?
|
||||||
map: Union[List[List[int]], List[List[str]]] = field(default_factory=list) # The actual map
|
map: list[list[int]] | list[list[str]] = field(default_factory=list) # The actual map
|
||||||
# Map attribs: MapName, MapVersion
|
data_misc: dict[str, str] = field(default_factory=dict) # <Data attribs>
|
||||||
# SupplierData attribs: ProductCode, RecipeName
|
supplier_data: dict[str, str] = field(default_factory=dict) # <SupplierData attribs>
|
||||||
|
|
||||||
misc: Dict[str, str] = field(default_factory=dict) # Any unexpected fields go here
|
misc: dict[str, str] = field(default_factory=dict) # Any unexpected fields go here
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def Rows(self) -> int:
|
def Rows(self) -> int:
|
||||||
@ -46,9 +45,9 @@ class Device:
|
|||||||
class Map:
|
class Map:
|
||||||
xmlns: str = 'http://www.semi.org'
|
xmlns: str = 'http://www.semi.org'
|
||||||
FormatRevision: str = "SEMI G85 0703"
|
FormatRevision: str = "SEMI G85 0703"
|
||||||
SubstrateType: Optional[str] = None
|
SubstrateType: str | None = None
|
||||||
SubstrateId: Optional[str] = None
|
SubstrateId: str | None = None
|
||||||
|
|
||||||
devices: List[Device] = field(default_factory=list)
|
devices: list[Device] = field(default_factory=list)
|
||||||
misc: Dict[str, str] = field(default_factory=dict) # Any unexpected fields go here
|
misc: dict[str, str] = field(default_factory=dict) # Any unexpected fields go here
|
||||||
|
|
||||||
|
45
g85/read.py
45
g85/read.py
@ -1,4 +1,4 @@
|
|||||||
from typing import List, Union, TextIO, Any
|
from typing import TextIO, Any
|
||||||
import logging
|
import logging
|
||||||
import datetime
|
import datetime
|
||||||
from dataclasses import fields
|
from dataclasses import fields
|
||||||
@ -10,7 +10,7 @@ from .main import Map, Device
|
|||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def read(stream: TextIO) -> List[Map]:
|
def read(stream: TextIO) -> list[Map]:
|
||||||
tree = ElementTree.parse(stream)
|
tree = ElementTree.parse(stream)
|
||||||
el_root = tree.getroot()
|
el_root = tree.getroot()
|
||||||
|
|
||||||
@ -21,7 +21,7 @@ def read(stream: TextIO) -> List[Map]:
|
|||||||
return maps
|
return maps
|
||||||
|
|
||||||
|
|
||||||
def read_wmaps(el_root: ElementTree.Element) -> List[Map]:
|
def read_wmaps(el_root: ElementTree.Element) -> list[Map]:
|
||||||
map_fields = [ff.name for ff in fields(Map)]
|
map_fields = [ff.name for ff in fields(Map)]
|
||||||
maps = []
|
maps = []
|
||||||
for el_map in el_root:
|
for el_map in el_root:
|
||||||
@ -41,7 +41,7 @@ def read_wmaps(el_root: ElementTree.Element) -> List[Map]:
|
|||||||
return maps
|
return maps
|
||||||
|
|
||||||
|
|
||||||
def read_devices(el_map: ElementTree.Element) -> List[Device]:
|
def read_devices(el_map: ElementTree.Element) -> list[Device]:
|
||||||
dev_fields = [ff.name for ff in fields(Device)]
|
dev_fields = [ff.name for ff in fields(Device)]
|
||||||
devices = []
|
devices = []
|
||||||
for el_device in el_map:
|
for el_device in el_map:
|
||||||
@ -50,7 +50,7 @@ def read_devices(el_map: ElementTree.Element) -> List[Device]:
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
bin_type = el_device.attrib['BinType']
|
bin_type = el_device.attrib['BinType']
|
||||||
null_bin: Union[int, str]
|
null_bin: int | str
|
||||||
if bin_type == 'Decimal':
|
if bin_type == 'Decimal':
|
||||||
null_bin = int(el_device.attrib['NullBin'])
|
null_bin = int(el_device.attrib['NullBin'])
|
||||||
else:
|
else:
|
||||||
@ -58,22 +58,24 @@ def read_devices(el_map: ElementTree.Element) -> List[Device]:
|
|||||||
|
|
||||||
device = Device(BinType=bin_type, NullBin=null_bin)
|
device = Device(BinType=bin_type, NullBin=null_bin)
|
||||||
|
|
||||||
val: Any
|
|
||||||
for key, val in el_device.attrib.items():
|
for key, val in el_device.attrib.items():
|
||||||
if key in ('BinType', 'NullBin'):
|
if key in ('BinType', 'NullBin'):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
parsed_val: Any
|
||||||
if key in ('WaferSize', 'DeviceSizeX', 'DeviceSizeY', 'Orientation'):
|
if key in ('WaferSize', 'DeviceSizeX', 'DeviceSizeY', 'Orientation'):
|
||||||
val = float(val)
|
parsed_val = float(val)
|
||||||
elif key in ('OriginLocation',):
|
elif key in ('OriginLocation',):
|
||||||
val = int(val)
|
parsed_val = int(val)
|
||||||
elif key == 'CreateDate':
|
elif key == 'CreateDate':
|
||||||
val = datetime.datetime.strptime(val + '000', '%Y%m%d%H%M%S%f')
|
parsed_val = datetime.datetime.strptime(val + '000', '%Y%m%d%H%M%S%f')
|
||||||
|
else:
|
||||||
|
parsed_val = val
|
||||||
|
|
||||||
if key in dev_fields and key[0].isupper():
|
if key in dev_fields and key[0].isupper():
|
||||||
setattr(device, key, val)
|
setattr(device, key, parsed_val)
|
||||||
else:
|
else:
|
||||||
device.misc[key] = val
|
device.misc[key] = parsed_val
|
||||||
|
|
||||||
for el_entry in el_device:
|
for el_entry in el_device:
|
||||||
tag = _tag(el_entry)
|
tag = _tag(el_entry)
|
||||||
@ -96,7 +98,12 @@ def read_devices(el_map: ElementTree.Element) -> List[Device]:
|
|||||||
f'with attributes {el_entry.attrib}')
|
f'with attributes {el_entry.attrib}')
|
||||||
continue
|
continue
|
||||||
|
|
||||||
bin_code = attrib['BinCode']
|
bin_code: int | str
|
||||||
|
if bin_type == 'Decimal':
|
||||||
|
bin_code = int(attrib['BinCode'])
|
||||||
|
else:
|
||||||
|
bin_code = attrib['BinCode']
|
||||||
|
|
||||||
if bin_code in device.bin_pass:
|
if bin_code in device.bin_pass:
|
||||||
logger.error(f'Bin code {bin_code} was repeated; ignoring later entry!')
|
logger.error(f'Bin code {bin_code} was repeated; ignoring later entry!')
|
||||||
continue
|
continue
|
||||||
@ -104,17 +111,23 @@ def read_devices(el_map: ElementTree.Element) -> List[Device]:
|
|||||||
device.bin_pass[bin_code] = attrib['BinQuality'].lower() == 'pass'
|
device.bin_pass[bin_code] = attrib['BinQuality'].lower() == 'pass'
|
||||||
elif tag == 'Data':
|
elif tag == 'Data':
|
||||||
data_strs = [read_row(rr) for rr in el_entry]
|
data_strs = [read_row(rr) for rr in el_entry]
|
||||||
data: Union[List[List[str]], List[List[int]]]
|
data: list[list[str]] | list[list[int]]
|
||||||
if device.BinType == 'Decimal':
|
if device.BinType == 'Decimal':
|
||||||
data = [[int(vv) for vv in rr] for rr in data_strs]
|
data = [[int(vv) for vv in rr] for rr in data_strs]
|
||||||
else:
|
else:
|
||||||
data = data_strs
|
data = data_strs
|
||||||
device.map = data
|
device.map = data
|
||||||
|
for key, value in attrib.items():
|
||||||
|
device.data_misc[key] = value
|
||||||
|
elif tag == 'SupplierData':
|
||||||
|
for key, value in attrib.items():
|
||||||
|
device.supplier_data[key] = value
|
||||||
|
|
||||||
devices.append(device)
|
devices.append(device)
|
||||||
return devices
|
return devices
|
||||||
|
|
||||||
|
|
||||||
def read_row(el_row: ElementTree.Element) -> List[str]:
|
def read_row(el_row: ElementTree.Element) -> list[str]:
|
||||||
assert _tag(el_row) == 'Row'
|
assert _tag(el_row) == 'Row'
|
||||||
|
|
||||||
row_stripped = (el_row.text or '').strip()
|
row_stripped = (el_row.text or '').strip()
|
||||||
@ -126,7 +139,7 @@ def read_row(el_row: ElementTree.Element) -> List[str]:
|
|||||||
|
|
||||||
|
|
||||||
def _tag(element: ElementTree.Element) -> str:
|
def _tag(element: ElementTree.Element) -> str:
|
||||||
'''
|
"""
|
||||||
Get the element's tag, excluding any namespaces.
|
Get the element's tag, excluding any namespaces.
|
||||||
'''
|
"""
|
||||||
return element.tag.split('}')[-1]
|
return element.tag.split('}')[-1]
|
||||||
|
67
g85/write.py
67
g85/write.py
@ -1,4 +1,5 @@
|
|||||||
from typing import Sequence, Tuple, List, TextIO, Union
|
from typing import TextIO, cast
|
||||||
|
from collections.abc import Sequence
|
||||||
import logging
|
import logging
|
||||||
import math
|
import math
|
||||||
from dataclasses import fields
|
from dataclasses import fields
|
||||||
@ -10,12 +11,15 @@ from .main import Map, Device
|
|||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class G85Error(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
# Hack to directly pass through <![CDATA[...]]>
|
# Hack to directly pass through <![CDATA[...]]>
|
||||||
def _escape_cdata(text):
|
def _escape_cdata(text: str) -> str:
|
||||||
if text.startswith('<![CDATA[') and text.endswith(']]>'):
|
if text.startswith('<![CDATA[') and text.endswith(']]>'):
|
||||||
return text
|
return text
|
||||||
else:
|
return _original_escape_cdata(text)
|
||||||
return _original_escape_cdata(text)
|
|
||||||
|
|
||||||
|
|
||||||
_original_escape_cdata = ElementTree._escape_cdata # type: ignore
|
_original_escape_cdata = ElementTree._escape_cdata # type: ignore
|
||||||
@ -41,8 +45,11 @@ def write_wmap(wmap: Map, el_root: ElementTree.Element) -> None:
|
|||||||
|
|
||||||
map_fields = [ff.name for ff in fields(wmap)]
|
map_fields = [ff.name for ff in fields(wmap)]
|
||||||
for field in map_fields:
|
for field in map_fields:
|
||||||
if field[0].isupper():
|
if field[0].isupper() or field == 'xmlns':
|
||||||
el_map.set(field, getattr(wmap, field))
|
val = getattr(wmap, field)
|
||||||
|
if val is None:
|
||||||
|
continue
|
||||||
|
el_map.set(field, val)
|
||||||
for key, value in wmap.misc.items():
|
for key, value in wmap.misc.items():
|
||||||
if key[0].isupper() and key in map_fields:
|
if key[0].isupper() and key in map_fields:
|
||||||
continue
|
continue
|
||||||
@ -61,7 +68,7 @@ def write_devices(devices: Sequence[Device], el_map: ElementTree.Element) -> Non
|
|||||||
|
|
||||||
# Row data prep
|
# Row data prep
|
||||||
if device.map is None:
|
if device.map is None:
|
||||||
raise Exception(f'No _data for device pformat({device})')
|
raise G85Error(f'No _data for device pformat({device})')
|
||||||
|
|
||||||
is_decimal = device.BinType == 'Decimal'
|
is_decimal = device.BinType == 'Decimal'
|
||||||
row_texts, bin_length = prepare_data(device.map, decimal=is_decimal)
|
row_texts, bin_length = prepare_data(device.map, decimal=is_decimal)
|
||||||
@ -81,8 +88,9 @@ def write_devices(devices: Sequence[Device], el_map: ElementTree.Element) -> Non
|
|||||||
el_bin.set('BinQuality', 'Pass' if passed else 'Fail')
|
el_bin.set('BinQuality', 'Pass' if passed else 'Fail')
|
||||||
el_bin.set('BinCount', str(bin_counts[bin_code]))
|
el_bin.set('BinCount', str(bin_counts[bin_code]))
|
||||||
|
|
||||||
|
el_data = ElementTree.SubElement(el_device, 'Data')
|
||||||
for row_text in row_texts:
|
for row_text in row_texts:
|
||||||
el_row = ElementTree.SubElement(el_device, 'Row')
|
el_row = ElementTree.SubElement(el_data, 'Row')
|
||||||
el_row.text = f'<![CDATA[{row_text}]]>'
|
el_row.text = f'<![CDATA[{row_text}]]>'
|
||||||
|
|
||||||
# Device attribs
|
# Device attribs
|
||||||
@ -90,6 +98,8 @@ def write_devices(devices: Sequence[Device], el_map: ElementTree.Element) -> Non
|
|||||||
for field in dev_fields:
|
for field in dev_fields:
|
||||||
if field[0].isupper():
|
if field[0].isupper():
|
||||||
val = getattr(device, field)
|
val = getattr(device, field)
|
||||||
|
if val is None:
|
||||||
|
continue
|
||||||
|
|
||||||
if field in ('WaferSize', 'DeviceSizeX', 'DeviceSizeY', 'Orientation'):
|
if field in ('WaferSize', 'DeviceSizeX', 'DeviceSizeY', 'Orientation'):
|
||||||
val = f'{val:g}'
|
val = f'{val:g}'
|
||||||
@ -97,6 +107,8 @@ def write_devices(devices: Sequence[Device], el_map: ElementTree.Element) -> Non
|
|||||||
val = f'{val:d}'
|
val = f'{val:d}'
|
||||||
elif field == 'CreateDate':
|
elif field == 'CreateDate':
|
||||||
val = val.strftime('%Y%m%d%H%M%S%f')[:-3]
|
val = val.strftime('%Y%m%d%H%M%S%f')[:-3]
|
||||||
|
elif field == 'NullBin' and device.BinType == 'Decimal':
|
||||||
|
val = f'{val:d}'
|
||||||
|
|
||||||
el_device.set(field, val)
|
el_device.set(field, val)
|
||||||
|
|
||||||
@ -105,27 +117,34 @@ def write_devices(devices: Sequence[Device], el_map: ElementTree.Element) -> Non
|
|||||||
continue
|
continue
|
||||||
el_device.set(key, value)
|
el_device.set(key, value)
|
||||||
|
|
||||||
|
for key, value in device.data_misc.items():
|
||||||
|
el_data.set(key, value)
|
||||||
|
|
||||||
def prepare_data(data: List[List[Union[str, int]]], decimal: bool) -> Tuple[List[str], int]:
|
if device.supplier_data:
|
||||||
|
el_suppdata = ElementTree.SubElement(el_device, 'SupplierData')
|
||||||
|
for key, value in device.data_misc.items():
|
||||||
|
el_suppdata.set(key, value)
|
||||||
|
|
||||||
|
|
||||||
|
def prepare_data(data: list[list[str]] | list[list[int]], decimal: bool) -> tuple[list[str], int]:
|
||||||
is_char = isinstance(data[0][0], str)
|
is_char = isinstance(data[0][0], str)
|
||||||
|
|
||||||
|
row_texts = []
|
||||||
if is_char:
|
if is_char:
|
||||||
|
data = cast(list[list[str]], data)
|
||||||
char_len = len(data[0][0])
|
char_len = len(data[0][0])
|
||||||
else:
|
for srow in data:
|
||||||
|
if char_len == 1:
|
||||||
|
row_text = ''.join(srow)
|
||||||
|
else:
|
||||||
|
row_text = ' '.join(srow) + ' '
|
||||||
|
row_texts.append(row_text)
|
||||||
|
return row_texts, char_len
|
||||||
|
else: # noqa: RET505
|
||||||
|
data = cast(list[list[int]], data)
|
||||||
max_value = max(max(rr) for rr in data)
|
max_value = max(max(rr) for rr in data)
|
||||||
max_digits = math.ceil(math.log10(max_value))
|
max_digits = math.ceil(math.log10(max_value))
|
||||||
|
for irow in data:
|
||||||
row_texts = []
|
row_text = ' '.join(str(vv).zfill(max_digits) for vv in irow) + ' '
|
||||||
for row in data:
|
row_texts.append(row_text)
|
||||||
if is_char and char_len == 1:
|
|
||||||
row_text = ''.join(row)
|
|
||||||
elif is_char:
|
|
||||||
row_text = ' '.join(row) + ' '
|
|
||||||
else:
|
|
||||||
row_text = ' '.join(str(vv).zfill(max_digits) for vv in row) + ' '
|
|
||||||
row_texts.append(row_text)
|
|
||||||
|
|
||||||
if is_char:
|
|
||||||
return row_texts, char_len
|
|
||||||
else:
|
|
||||||
return row_texts, max_digits
|
return row_texts, max_digits
|
||||||
|
78
pyproject.toml
Normal file
78
pyproject.toml
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
[build-system]
|
||||||
|
requires = ["hatchling"]
|
||||||
|
build-backend = "hatchling.build"
|
||||||
|
|
||||||
|
[project]
|
||||||
|
name = "g85"
|
||||||
|
description = "G85 wafer map reader / writer"
|
||||||
|
readme = "README.md"
|
||||||
|
license = { file = "LICENSE.md" }
|
||||||
|
authors = [
|
||||||
|
{ name="Jan Petykiewicz", email="jan@mpxd.net" },
|
||||||
|
]
|
||||||
|
homepage = "https://mpxd.net/code/jan/g85"
|
||||||
|
repository = "https://mpxd.net/code/jan/g85"
|
||||||
|
keywords = [
|
||||||
|
"design",
|
||||||
|
"CAD",
|
||||||
|
"EDA",
|
||||||
|
"electronics",
|
||||||
|
"photonics",
|
||||||
|
"IC",
|
||||||
|
"mask",
|
||||||
|
"wafer",
|
||||||
|
"map",
|
||||||
|
"G85",
|
||||||
|
"wmap",
|
||||||
|
]
|
||||||
|
classifiers = [
|
||||||
|
"Programming Language :: Python :: 3",
|
||||||
|
"Development Status :: 4 - Beta",
|
||||||
|
"Intended Audience :: Developers",
|
||||||
|
"Intended Audience :: Information Technology",
|
||||||
|
"Intended Audience :: Manufacturing",
|
||||||
|
"Intended Audience :: Science/Research",
|
||||||
|
"License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
|
||||||
|
"Topic :: Scientific/Engineering :: Electronic Design Automation (EDA)",
|
||||||
|
]
|
||||||
|
requires-python = ">=3.10"
|
||||||
|
dynamic = ["version"]
|
||||||
|
dependencies = [
|
||||||
|
]
|
||||||
|
|
||||||
|
[tool.hatch.version]
|
||||||
|
path = "g85/__init__.py"
|
||||||
|
|
||||||
|
|
||||||
|
[tool.ruff]
|
||||||
|
exclude = [
|
||||||
|
".git",
|
||||||
|
"dist",
|
||||||
|
]
|
||||||
|
line-length = 145
|
||||||
|
indent-width = 4
|
||||||
|
lint.dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"
|
||||||
|
lint.select = [
|
||||||
|
"NPY", "E", "F", "W", "B", "ANN", "UP", "SLOT", "SIM", "LOG",
|
||||||
|
"C4", "ISC", "PIE", "PT", "RET", "TCH", "PTH", "INT",
|
||||||
|
"ARG", "PL", "R", "TRY",
|
||||||
|
"G010", "G101", "G201", "G202",
|
||||||
|
"Q002", "Q003", "Q004",
|
||||||
|
]
|
||||||
|
lint.ignore = [
|
||||||
|
#"ANN001", # No annotation
|
||||||
|
"ANN002", # *args
|
||||||
|
"ANN003", # **kwargs
|
||||||
|
"ANN401", # Any
|
||||||
|
"ANN101", # self: Self
|
||||||
|
"SIM108", # single-line if / else assignment
|
||||||
|
"RET504", # x=y+z; return x
|
||||||
|
"PIE790", # unnecessary pass
|
||||||
|
"ISC003", # non-implicit string concatenation
|
||||||
|
"C408", # dict(x=y) instead of {'x': y}
|
||||||
|
"PLR09", # Too many xxx
|
||||||
|
"PLR2004", # magic number
|
||||||
|
"PLC0414", # import x as x
|
||||||
|
"TRY003", # Long exception message
|
||||||
|
]
|
||||||
|
|
51
setup.py
51
setup.py
@ -1,51 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
|
|
||||||
from setuptools import setup, find_packages
|
|
||||||
|
|
||||||
|
|
||||||
with open('README.md', 'r') as f:
|
|
||||||
long_description = f.read()
|
|
||||||
|
|
||||||
with open('g85/VERSION.py', 'rt') as f:
|
|
||||||
version = f.readlines()[2].strip()
|
|
||||||
|
|
||||||
setup(
|
|
||||||
name='g85',
|
|
||||||
version=version,
|
|
||||||
description='G85 wafer map reader / writer',
|
|
||||||
long_description=long_description,
|
|
||||||
long_description_content_type='text/markdown',
|
|
||||||
author='Jan Petykiewicz',
|
|
||||||
author_email='jan@mpxd.net',
|
|
||||||
url='https://mpxd.net/code/jan/g85',
|
|
||||||
packages=find_packages(),
|
|
||||||
package_data={
|
|
||||||
'g85': ['py.typed'],
|
|
||||||
},
|
|
||||||
install_requires=[
|
|
||||||
'numpy',
|
|
||||||
],
|
|
||||||
classifiers=[
|
|
||||||
'Programming Language :: Python :: 3',
|
|
||||||
'Development Status :: 4 - Beta',
|
|
||||||
'Intended Audience :: Developers',
|
|
||||||
'Intended Audience :: Information Technology',
|
|
||||||
'Intended Audience :: Manufacturing',
|
|
||||||
'Intended Audience :: Science/Research',
|
|
||||||
'License :: OSI Approved :: GNU General Public License v3 (GPLv3)',
|
|
||||||
'Topic :: Scientific/Engineering :: Electronic Design Automation (EDA)',
|
|
||||||
],
|
|
||||||
keywords=[
|
|
||||||
'design',
|
|
||||||
'CAD',
|
|
||||||
'EDA',
|
|
||||||
'electronics',
|
|
||||||
'photonics',
|
|
||||||
'IC',
|
|
||||||
'mask',
|
|
||||||
'wafer',
|
|
||||||
'map',
|
|
||||||
'G85',
|
|
||||||
'wmap',
|
|
||||||
],
|
|
||||||
)
|
|
Loading…
x
Reference in New Issue
Block a user