msx-charset 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Your Name
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,34 @@
1
+ # MANIFEST.in
2
+ include LICENSE
3
+
4
+ # Include README
5
+ include README.md
6
+
7
+ # Include all Python files
8
+ include msx_charset/*.py
9
+ include msx_charset/tests/*.py
10
+
11
+ # Include test data if any
12
+ include msx_charset/tests/data/*
13
+
14
+ # Include documentation
15
+ include docs/*.md
16
+ include docs/*.rst
17
+
18
+ # Include type hints
19
+ include msx_charset/py.typed
20
+
21
+ # Exclude unnecessary files
22
+ exclude .git*
23
+ exclude .github/*
24
+ exclude .vscode/*
25
+ exclude __pycache__/*
26
+ exclude *.pyc
27
+ exclude *.pyo
28
+ exclude *.pyd
29
+ exclude .coverage
30
+ exclude .pytest_cache/*
31
+ exclude .tox/*
32
+ exclude build/*
33
+ exclude dist/*
34
+ exclude *.egg-info/*
@@ -0,0 +1,117 @@
1
+ Metadata-Version: 2.4
2
+ Name: msx-charset
3
+ Version: 0.1.0
4
+ Summary: MSX character set codecs for Python
5
+ Home-page: https://github.com/yamamo-to/msx-charset
6
+ Author: yamamo-to
7
+ Author-email: yamamo-to <humorum@gmail.com>
8
+ License-Expression: MIT
9
+ Project-URL: Repository, https://github.com/yamamo-to/msx-charset
10
+ Project-URL: Issue, https://github.com/yamamo-to/msx-charset/issues
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.8
15
+ Classifier: Programming Language :: Python :: 3.9
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
19
+ Requires-Python: >=3.8
20
+ Description-Content-Type: text/markdown
21
+ Dynamic: author
22
+ Dynamic: home-page
23
+ Dynamic: requires-python
24
+
25
+ # MSX Character Set Converter
26
+
27
+ [![Test](https://github.com/yamamo-to/msx-charset/actions/workflows/test.yml/badge.svg)](https://github.com/yamamo-to/msx-charset/actions/workflows/test.yml)
28
+
29
+ A Python package for converting between Unicode and MSX character sets, supporting Japanese, International, and Brazilian character sets.
30
+
31
+ ## Features
32
+
33
+ - Support for MSX character set conversion:
34
+ - Japanese version
35
+ - International version
36
+ - Brazilian version
37
+ - Compatible with Python 3.8 and later
38
+
39
+ ## Installation
40
+
41
+ ```bash
42
+ pip install msx-charset
43
+ ```
44
+
45
+ ## Usage
46
+
47
+ ### Japanese Character Set
48
+
49
+ ```python
50
+ import msx_charset
51
+
52
+ # Encoding
53
+ text = "こんにちは"
54
+ encoded = text.encode('msx-jp')
55
+ print(encoded) # MSX code byte sequence
56
+
57
+ # Decoding
58
+ decoded = encoded.decode('msx-jp')
59
+ print(decoded) # "こんにちは"
60
+ ```
61
+
62
+ ### International Character Set
63
+
64
+ ```python
65
+ import msx_charset
66
+
67
+ # Encoding
68
+ text = "áéíóú"
69
+ encoded = text.encode('msx-intl')
70
+ print(encoded) # MSX code byte sequence
71
+
72
+ # Decoding
73
+ decoded = encoded.decode('msx-intl')
74
+ print(decoded) # "áéíóú"
75
+ ```
76
+
77
+ ### Brazilian Character Set
78
+
79
+ ```python
80
+ import msx_charset
81
+
82
+ # Encoding
83
+ text = "Olá, mundo!"
84
+ encoded = text.encode('msx-br')
85
+ print(encoded) # MSX code byte sequence
86
+
87
+ # Decoding
88
+ decoded = encoded.decode('msx-br')
89
+ print(decoded) # "Olá, mundo!"
90
+ ```
91
+
92
+ ## Development
93
+
94
+ ### Setup
95
+
96
+ ```bash
97
+ # Create virtual environment
98
+ python -m venv venv
99
+ source venv/bin/activate # Windows: venv\Scripts\activate
100
+
101
+ # Install development package
102
+ pip install -e .
103
+ ```
104
+
105
+ ### Running Tests
106
+
107
+ ```bash
108
+ python -m unittest discover -s msx_charset/tests
109
+ ```
110
+
111
+ ## Contributing
112
+
113
+ Contributions are welcome! Please feel free to submit a Pull Request.
114
+
115
+ ## License
116
+
117
+ MIT License
@@ -0,0 +1,93 @@
1
+ # MSX Character Set Converter
2
+
3
+ [![Test](https://github.com/yamamo-to/msx-charset/actions/workflows/test.yml/badge.svg)](https://github.com/yamamo-to/msx-charset/actions/workflows/test.yml)
4
+
5
+ A Python package for converting between Unicode and MSX character sets, supporting Japanese, International, and Brazilian character sets.
6
+
7
+ ## Features
8
+
9
+ - Support for MSX character set conversion:
10
+ - Japanese version
11
+ - International version
12
+ - Brazilian version
13
+ - Compatible with Python 3.8 and later
14
+
15
+ ## Installation
16
+
17
+ ```bash
18
+ pip install msx-charset
19
+ ```
20
+
21
+ ## Usage
22
+
23
+ ### Japanese Character Set
24
+
25
+ ```python
26
+ import msx_charset
27
+
28
+ # Encoding
29
+ text = "こんにちは"
30
+ encoded = text.encode('msx-jp')
31
+ print(encoded) # MSX code byte sequence
32
+
33
+ # Decoding
34
+ decoded = encoded.decode('msx-jp')
35
+ print(decoded) # "こんにちは"
36
+ ```
37
+
38
+ ### International Character Set
39
+
40
+ ```python
41
+ import msx_charset
42
+
43
+ # Encoding
44
+ text = "áéíóú"
45
+ encoded = text.encode('msx-intl')
46
+ print(encoded) # MSX code byte sequence
47
+
48
+ # Decoding
49
+ decoded = encoded.decode('msx-intl')
50
+ print(decoded) # "áéíóú"
51
+ ```
52
+
53
+ ### Brazilian Character Set
54
+
55
+ ```python
56
+ import msx_charset
57
+
58
+ # Encoding
59
+ text = "Olá, mundo!"
60
+ encoded = text.encode('msx-br')
61
+ print(encoded) # MSX code byte sequence
62
+
63
+ # Decoding
64
+ decoded = encoded.decode('msx-br')
65
+ print(decoded) # "Olá, mundo!"
66
+ ```
67
+
68
+ ## Development
69
+
70
+ ### Setup
71
+
72
+ ```bash
73
+ # Create virtual environment
74
+ python -m venv venv
75
+ source venv/bin/activate # Windows: venv\Scripts\activate
76
+
77
+ # Install development package
78
+ pip install -e .
79
+ ```
80
+
81
+ ### Running Tests
82
+
83
+ ```bash
84
+ python -m unittest discover -s msx_charset/tests
85
+ ```
86
+
87
+ ## Contributing
88
+
89
+ Contributions are welcome! Please feel free to submit a Pull Request.
90
+
91
+ ## License
92
+
93
+ MIT License
@@ -0,0 +1,21 @@
1
+ import codecs
2
+ from .codec_jp import msx_codec_jp
3
+ from .codec_intl import msx_codec_intl
4
+ from .codec_br import msx_codec_br
5
+
6
+ __version__ = "0.1.0"
7
+ __all__ = ["msx_codec_jp", "msx_codec_intl", "msx_codec_br"]
8
+
9
+
10
+ def msx_charset(name):
11
+ if name.lower() in ('msx-jp', 'msx_jp'):
12
+ return msx_codec_jp
13
+ elif name.lower() in ('msx-intl', 'msx_intl'):
14
+ return msx_codec_intl
15
+ elif name.lower() in ('msx-br', 'msx_br'):
16
+ return msx_codec_br
17
+ raise LookupError(f"unknown encoding: {name}")
18
+
19
+
20
+ # Register codec
21
+ codecs.register(msx_charset)
@@ -0,0 +1,60 @@
1
+ import codecs
2
+
3
+ class MSXCodecBase:
4
+ """Base class for MSX character set codecs"""
5
+
6
+ def __init__(self, name, custom_map, graphic_map):
7
+ self.name = name
8
+
9
+ # ASCII (0x00-0x7F) range mapping
10
+ self.ascii_map = {
11
+ i: chr(i) for i in range(0x80)
12
+ if chr(i).isprintable() or chr(i) in '\x00\x07\x08\x09\x0A\x0D\x1B\x20'
13
+ }
14
+
15
+ # Character mappings
16
+ self.char_map = {**self.ascii_map, **custom_map}
17
+ self.char_reverse_map = {v: k for k, v in self.char_map.items()}
18
+ self.graphic_map = graphic_map
19
+ self.graphic_reverse_map = {v: k for k, v in graphic_map.items()}
20
+
21
+ # Create codec info
22
+ self.codec_info = codecs.CodecInfo(
23
+ name=name,
24
+ encode=self.encode,
25
+ decode=self.decode
26
+ )
27
+
28
+ def encode(self, text, errors='strict'):
29
+ """Convert UTF-8 string to MSX code byte sequence"""
30
+ result = bytearray()
31
+ for char in text:
32
+ if char in self.graphic_map:
33
+ result.extend(self.graphic_map[char])
34
+ else:
35
+ result.append(self.char_reverse_map.get(char, 0x3F))
36
+ return bytes(result), len(text)
37
+
38
+ def decode(self, codes, errors='strict'):
39
+ """Convert MSX code byte sequence to UTF-8 string"""
40
+ result = []
41
+ i = 0
42
+ codes_len = len(codes)
43
+
44
+ while i < codes_len:
45
+ code = codes[i]
46
+
47
+ # Handle graphic characters
48
+ if code == 0x01 and i + 1 < codes_len:
49
+ next_code = codes[i + 1]
50
+ char = self.graphic_reverse_map.get((code, next_code))
51
+ if char:
52
+ result.append(char)
53
+ i += 2
54
+ continue
55
+
56
+ # Handle regular characters
57
+ result.append(self.char_map.get(code, '?'))
58
+ i += 1
59
+
60
+ return ''.join(result), len(codes)
@@ -0,0 +1,84 @@
1
+ import codecs
2
+ from .codec_base import MSXCodecBase
3
+
4
+ # Define data first
5
+ # ASCII (0x00-0x7F) range mapping (some control characters are allowed)
6
+ MSX_ASCII_MAP = {
7
+ i: chr(i) for i in range(0x80)
8
+ if chr(i).isprintable() or chr(i) in '\x00\x07\x08\x09\x0A\x0D\x1B\x20'
9
+ }
10
+
11
+ # MSX Brazilian
12
+ MSX_CUSTOM_MAP_BR = {
13
+ 0x80: 'Ç', 0x81: 'ü', 0x82: 'é', 0x83: 'â',
14
+ 0x84: 'Á', 0x85: 'à', 0x86: '¨', 0x87: 'ç',
15
+ 0x88: 'ê', 0x89: 'Í', 0x8A: 'Ó', 0x8B: 'Ú',
16
+ 0x8C: 'Â', 0x8D: 'Ê', 0x8E: 'Ô', 0x8F: 'À',
17
+ 0x90: 'É', 0x91: 'æ', 0x92: 'Æ', 0x93: 'ô',
18
+ 0x94: 'ö', 0x95: 'ò', 0x96: 'û', 0x97: 'ù',
19
+ 0x98: 'ÿ', 0x99: 'Ö', 0x9A: 'Ü', 0x9B: '¢',
20
+ 0x9C: '£', 0x9D: '¥', 0x9E: 'Cz', 0x9F: 'ƒ',
21
+
22
+ 0xA0: 'á', 0xA1: 'í', 0xA2: 'ó', 0xA3: 'ú',
23
+ 0xA4: 'ñ', 0xA5: 'Ñ', 0xA6: 'ª', 0xA7: 'º',
24
+ 0xA8: '¿', 0xA9: '⌐', 0xAA: '¬', 0xAB: '½',
25
+ 0xAC: '¼', 0xAD: '¡', 0xAE: '«', 0xAF: '»',
26
+ 0xB0: 'Ã', 0xB1: 'ã', 0xB2: 'Ĩ', 0xB3: 'ĩ',
27
+ 0xB4: 'Õ', 0xB5: 'õ', 0xB6: 'Ű', 0xB7: 'ű',
28
+ 0xB8: 'IJ', 0xB9: 'ij', 0xBA: '¾', 0xBB: '∽',
29
+ 0xBC: '◊', 0xBD: '‰', 0xBE: '¶', 0xBF: '§',
30
+
31
+ 0xC0: '▂', 0xC1: '▚', 0xC2: '▆', 0xC3: '🮂',
32
+ 0xC4: '▬', 0xC5: '🮅', 0xC6: '▎', 0xC7: '▞',
33
+ 0xC8: '▊', 0xC9: '🮇', 0xCA: '🮊', 0xCB: '🮙',
34
+ 0xCC: '🮘', 0xCD: '🭭', 0xCE: '🭯', 0xCF: '🭬',
35
+ 0xD0: '🭮', 0xD1: '🮚', 0xD2: '🮛', 0xD3: '▘',
36
+ 0xD4: '▗', 0xD5: '▝', 0xD6: '▖', 0xD7: '🮖',
37
+ 0xD8: 'Δ', 0xD9: '‡', 0xDA: 'ω', 0xDB: '█',
38
+ 0xDC: '▄', 0xDD: '▌', 0xDE: '▐', 0xDF: '▀',
39
+
40
+ 0xE0: 'α', 0xE1: 'ß', 0xE2: 'Γ', 0xE3: 'π',
41
+ 0xE4: 'Σ', 0xE5: 'σ', 0xE6: 'µ', 0xE7: 'τ',
42
+ 0xE8: 'Φ', 0xE9: 'Θ', 0xEA: 'Ω', 0xEB: 'δ',
43
+ 0xEC: '∞', 0xED: '⌀', 0xEE: '∈', 0xEF: '∩',
44
+ 0xF0: '≡', 0xF1: '±', 0xF2: '≥', 0xF3: '≤',
45
+ 0xF4: '⌠', 0xF5: '⌡', 0xF6: '÷', 0xF7: '≈',
46
+ 0xF8: '°', 0xF9: '∙', 0xFA: '·', 0xFB: '√',
47
+ 0xFC: 'ⁿ', 0xFD: '²', 0xFE: '■'
48
+ }
49
+
50
+ # Combined map (MSX code to character)
51
+ MSX_CHAR_MAP_BR = dict(MSX_ASCII_MAP)
52
+ MSX_CHAR_MAP_BR.update(MSX_CUSTOM_MAP_BR)
53
+
54
+ # Reverse lookup (character to MSX code)
55
+ MSX_CHAR_REVERSE_MAP_BR = {v: k for k, v in MSX_CHAR_MAP_BR.items()}
56
+
57
+ # Graphic character mapping (2-byte characters)
58
+ GRAPHIC_CHAR_MAP_BR = {
59
+ '☺': (0x01, 0x41), '☻': (0x01, 0x42), '♥': (0x01, 0x43),
60
+ '♦': (0x01, 0x44), '♣': (0x01, 0x45), '♠': (0x01, 0x46),
61
+ '•': (0x01, 0x47), '◘': (0x01, 0x48), '○': (0x01, 0x49),
62
+ '◙': (0x01, 0x4A), '♂': (0x01, 0x4B), '♀': (0x01, 0x4C),
63
+ '♪': (0x01, 0x4D), '♫': (0x01, 0x4E), '☼': (0x01, 0x4F),
64
+ '⟊': (0x01, 0x50), '┴': (0x01, 0x51), '┬': (0x01, 0x52),
65
+ '┤': (0x01, 0x53), '├': (0x01, 0x54), '┼': (0x01, 0x55),
66
+ '│': (0x01, 0x56), '─': (0x01, 0x57), '┌': (0x01, 0x58),
67
+ '┐': (0x01, 0x59), '└': (0x01, 0x5A), '┘': (0x01, 0x5B),
68
+ '╳': (0x01, 0x5C), '╱': (0x01, 0x5D), '╲': (0x01, 0x5E),
69
+ '🮯': (0x01, 0x5F)
70
+ }
71
+
72
+ # Reverse lookup for graphic characters
73
+ GRAPHIC_CHAR_REVERSE_MAP_BR = {v: k for k, v in GRAPHIC_CHAR_MAP_BR.items()}
74
+
75
+
76
+ class MSXBrazilianCodec(MSXCodecBase):
77
+ """Brazilian MSX character set codec"""
78
+
79
+ def __init__(self):
80
+ super().__init__('msx-br', MSX_CUSTOM_MAP_BR, GRAPHIC_CHAR_MAP_BR)
81
+
82
+
83
+ # Create codec instance
84
+ msx_codec_br = MSXBrazilianCodec().codec_info
@@ -0,0 +1,85 @@
1
+ import codecs
2
+ from .codec_base import MSXCodecBase
3
+
4
+ # Define data first
5
+ # ASCII (0x00-0x7F) range mapping (some control characters are allowed)
6
+ MSX_ASCII_MAP = {
7
+ i: chr(i) for i in range(0x80)
8
+ if chr(i).isprintable() or chr(i) in '\x00\x07\x08\x09\x0A\x0D\x1B\x20'
9
+ }
10
+
11
+ # MSX International
12
+ MSX_CUSTOM_MAP_INTL = {
13
+ 0x80: 'Ç', 0x81: 'ü', 0x82: 'é', 0x83: 'â',
14
+ 0x84: 'ä', 0x85: 'à', 0x86: 'å', 0x87: 'ç',
15
+ 0x88: 'ê', 0x89: 'ë', 0x8A: 'è', 0x8B: 'ï',
16
+ 0x8C: 'î', 0x8D: 'ì', 0x8E: 'Ä', 0x8F: 'Å',
17
+ 0x90: 'É', 0x91: 'æ', 0x92: 'Æ', 0x93: 'ô',
18
+ 0x94: 'ö', 0x95: 'ò', 0x96: 'û', 0x97: 'ù',
19
+ 0x98: 'ÿ', 0x99: 'Ö', 0x9A: 'Ü', 0x9B: '¢',
20
+ 0x9C: '£', 0x9D: '¥', 0x9E: '₧', 0x9F: 'ƒ',
21
+
22
+ 0xA0: 'á', 0xA1: 'í', 0xA2: 'ó', 0xA3: 'ú',
23
+ 0xA4: 'ñ', 0xA5: 'Ñ', 0xA6: 'ª', 0xA7: 'º',
24
+ 0xA8: '¿', 0xA9: '⌐', 0xAA: '¬', 0xAB: '½',
25
+ 0xAC: '¼', 0xAD: '¡', 0xAE: '«', 0xAF: '»',
26
+ 0xB0: 'Ã', 0xB1: 'ã', 0xB2: 'Ĩ', 0xB3: 'ĩ',
27
+ 0xB4: 'Õ', 0xB5: 'õ', 0xB6: 'Ű', 0xB7: 'ű',
28
+ 0xB8: 'IJ', 0xB9: 'ij', 0xBA: '¾', 0xBB: '∽',
29
+ 0xBC: '◊', 0xBD: '‰', 0xBE: '¶', 0xBF: '§',
30
+
31
+ 0xC0: '▂', 0xC1: '▚', 0xC2: '▆', 0xC3: '🮂',
32
+ 0xC4: '▬', 0xC5: '🮅', 0xC6: '▎', 0xC7: '▞',
33
+ 0xC8: '▊', 0xC9: '🮇', 0xCA: '🮊', 0xCB: '🮙',
34
+ 0xCC: '🮘', 0xCD: '🭭', 0xCE: '🭯', 0xCF: '🭬',
35
+ 0xD0: '🭮', 0xD1: '🮚', 0xD2: '🮛', 0xD3: '▘',
36
+ 0xD4: '▗', 0xD5: '▝', 0xD6: '▖', 0xD7: '🮖',
37
+ 0xD8: 'Δ', 0xD9: '‡', 0xDA: 'ω', 0xDB: '█',
38
+ 0xDC: '▄', 0xDD: '▌', 0xDE: '▐', 0xDF: '▀',
39
+
40
+ 0xE0: 'α', 0xE1: 'ß', 0xE2: 'Γ', 0xE3: 'π',
41
+ 0xE4: 'Σ', 0xE5: 'σ', 0xE6: 'µ', 0xE7: 'τ',
42
+ 0xE8: 'Φ', 0xE9: 'Θ', 0xEA: 'Ω', 0xEB: 'δ',
43
+ 0xEC: '∞', 0xED: '⌀', 0xEE: '∈', 0xEF: '∩',
44
+ 0xF0: '≡', 0xF1: '±', 0xF2: '≥', 0xF3: '≤',
45
+ 0xF4: '⌠', 0xF5: '⌡', 0xF6: '÷', 0xF7: '≈',
46
+ 0xF8: '°', 0xF9: '∙', 0xFA: '·', 0xFB: '√',
47
+ 0xFC: 'ⁿ', 0xFD: '²', 0xFE: '■'
48
+ }
49
+
50
+ # Combined map (MSX code to character)
51
+ MSX_CHAR_MAP_INTL = dict(MSX_ASCII_MAP)
52
+ MSX_CHAR_MAP_INTL.update(MSX_CUSTOM_MAP_INTL)
53
+
54
+ # Reverse lookup (character to MSX code)
55
+ MSX_CHAR_REVERSE_MAP_INTL = {v: k for k, v in MSX_CHAR_MAP_INTL.items()}
56
+
57
+
58
+ # Graphic character mapping (2-byte characters)
59
+ GRAPHIC_CHAR_MAP_INTL = {
60
+ '☺': (0x01, 0x41), '☻': (0x01, 0x42), '♥': (0x01, 0x43),
61
+ '♦': (0x01, 0x44), '♣': (0x01, 0x45), '♠': (0x01, 0x46),
62
+ '•': (0x01, 0x47), '◘': (0x01, 0x48), '○': (0x01, 0x49),
63
+ '◙': (0x01, 0x4A), '♂': (0x01, 0x4B), '♀': (0x01, 0x4C),
64
+ '♪': (0x01, 0x4D), '♫': (0x01, 0x4E), '☼': (0x01, 0x4F),
65
+ '⟊': (0x01, 0x50), '┴': (0x01, 0x51), '┬': (0x01, 0x52),
66
+ '┤': (0x01, 0x53), '├': (0x01, 0x54), '┼': (0x01, 0x55),
67
+ '│': (0x01, 0x56), '─': (0x01, 0x57), '┌': (0x01, 0x58),
68
+ '┐': (0x01, 0x59), '└': (0x01, 0x5A), '┘': (0x01, 0x5B),
69
+ '╳': (0x01, 0x5C), '╱': (0x01, 0x5D), '╲': (0x01, 0x5E),
70
+ '🮯': (0x01, 0x5F)
71
+ }
72
+
73
+ # Reverse lookup for graphic characters
74
+ GRAPHIC_CHAR_REVERSE_MAP_INTL = {v: k for k, v in GRAPHIC_CHAR_MAP_INTL.items()}
75
+
76
+
77
+ class MSXInternationalCodec(MSXCodecBase):
78
+ """International MSX character set codec"""
79
+
80
+ def __init__(self):
81
+ super().__init__('msx-intl', MSX_CUSTOM_MAP_INTL, GRAPHIC_CHAR_MAP_INTL)
82
+
83
+
84
+ # Create codec instance
85
+ msx_codec_intl = MSXInternationalCodec().codec_info
@@ -0,0 +1,144 @@
1
+ from .codec_base import MSXCodecBase
2
+
3
+ # MSX-specific Japanese and symbol characters (0x80-0xFE)
4
+ MSX_CUSTOM_MAP_JP = {
5
+ # Column 1 (0x80-0x9F): Special characters and hiragana
6
+ 0x80: '♠', 0x81: '♥', 0x82: '♣', 0x83: '♦',
7
+ 0x84: '○', 0x85: '●', 0x86: 'を', 0x87: 'ぁ',
8
+ 0x88: 'ぃ', 0x89: 'ぅ', 0x8A: 'ぇ', 0x8B: 'ぉ',
9
+ 0x8C: 'ゃ', 0x8D: 'ゅ', 0x8E: 'ょ', 0x8F: 'っ',
10
+ 0x90: ' ', 0x91: 'あ', 0x92: 'い', 0x93: 'う',
11
+ 0x94: 'え', 0x95: 'お', 0x96: 'か', 0x97: 'き',
12
+ 0x98: 'く', 0x99: 'け', 0x9A: 'こ', 0x9B: 'さ',
13
+ 0x9C: 'し', 0x9D: 'す', 0x9E: 'せ', 0x9F: 'そ',
14
+
15
+ # Column 2 (0xA0-0xBF): Symbols and katakana
16
+ 0xA0: ' ', 0xA1: '。', 0xA2: '「', 0xA3: '」',
17
+ 0xA4: '、', 0xA5: '・', 0xA6: 'ヲ', 0xA7: 'ァ',
18
+ 0xA8: 'ィ', 0xA9: 'ゥ', 0xAA: 'ェ', 0xAB: 'ォ',
19
+ 0xAC: 'ャ', 0xAD: 'ュ', 0xAE: 'ョ', 0xAF: 'ッ',
20
+ 0xB0: 'ー', 0xB1: 'ア', 0xB2: 'イ', 0xB3: 'ウ',
21
+ 0xB4: 'エ', 0xB5: 'オ', 0xB6: 'カ', 0xB7: 'キ',
22
+ 0xB8: 'ク', 0xB9: 'ケ', 0xBA: 'コ', 0xBB: 'サ',
23
+ 0xBC: 'シ', 0xBD: 'ス', 0xBE: 'セ', 0xBF: 'ソ',
24
+
25
+ # Column 3 (0xC0-0xDF): Katakana (continued)
26
+ 0xC0: 'タ', 0xC1: 'チ', 0xC2: 'ツ', 0xC3: 'テ',
27
+ 0xC4: 'ト', 0xC5: 'ナ', 0xC6: 'ニ', 0xC7: 'ヌ',
28
+ 0xC8: 'ネ', 0xC9: 'ノ', 0xCA: 'ハ', 0xCB: 'ヒ',
29
+ 0xCC: 'フ', 0xCD: 'ヘ', 0xCE: 'ホ', 0xCF: 'マ',
30
+ 0xD0: 'ミ', 0xD1: 'ム', 0xD2: 'メ', 0xD3: 'モ',
31
+ 0xD4: 'ヤ', 0xD5: 'ユ', 0xD6: 'ヨ', 0xD7: 'ラ',
32
+ 0xD8: 'リ', 0xD9: 'ル', 0xDA: 'レ', 0xDB: 'ロ',
33
+ 0xDC: 'ワ', 0xDD: 'ン', 0xDE: '゛', 0xDF: '゜',
34
+
35
+ # Column 4 (0xE0-0xFE): Hiragana (continued)
36
+ 0xE0: 'た', 0xE1: 'ち', 0xE2: 'つ', 0xE3: 'て',
37
+ 0xE4: 'と', 0xE5: 'な', 0xE6: 'に', 0xE7: 'ぬ',
38
+ 0xE8: 'ね', 0xE9: 'の', 0xEA: 'は', 0xEB: 'ひ',
39
+ 0xEC: 'ふ', 0xED: 'へ', 0xEE: 'ほ', 0xEF: 'ま',
40
+ 0xF0: 'み', 0xF1: 'む', 0xF2: 'め', 0xF3: 'も',
41
+ 0xF4: 'や', 0xF5: 'ゆ', 0xF6: 'よ', 0xF7: 'ら',
42
+ 0xF8: 'り', 0xF9: 'る', 0xFA: 'れ', 0xFB: 'ろ',
43
+ 0xFC: 'わ', 0xFD: 'ん', 0xFE: '!' # Full-width exclamation mark
44
+ }
45
+
46
+ # Graphic character mapping (2-byte characters)
47
+ GRAPHIC_CHAR_MAP_JP = {
48
+ '月': (0x01, 0x41), '火': (0x01, 0x42), '水': (0x01, 0x43),
49
+ '木': (0x01, 0x44), '金': (0x01, 0x45), '土': (0x01, 0x46),
50
+ '日': (0x01, 0x47), '年': (0x01, 0x48), '円': (0x01, 0x49),
51
+ '時': (0x01, 0x4A), '分': (0x01, 0x4B), '秒': (0x01, 0x4C),
52
+ '百': (0x01, 0x4D), '千': (0x01, 0x4E), '万': (0x01, 0x4F),
53
+ 'π': (0x01, 0x50), '┴': (0x01, 0x51), '┬': (0x01, 0x52),
54
+ '┤': (0x01, 0x53), '├': (0x01, 0x54), '┼': (0x01, 0x55),
55
+ '│': (0x01, 0x56), '─': (0x01, 0x57), '┌': (0x01, 0x58),
56
+ '┐': (0x01, 0x59), '└': (0x01, 0x5A), '┘': (0x01, 0x5B),
57
+ '×': (0x01, 0x5C), '大': (0x01, 0x5D), '中': (0x01, 0x5E),
58
+ '小': (0x01, 0x5F)
59
+ }
60
+
61
+ # Voiced sound mark and semi-voiced sound mark mapping
62
+ DAKUTEN_MAP = {
63
+ 'が': ('か', 0xDE), 'ぎ': ('き', 0xDE), 'ぐ': ('く', 0xDE),
64
+ 'げ': ('け', 0xDE), 'ご': ('こ', 0xDE),
65
+ 'ざ': ('さ', 0xDE), 'じ': ('し', 0xDE), 'ず': ('す', 0xDE),
66
+ 'ぜ': ('せ', 0xDE), 'ぞ': ('そ', 0xDE),
67
+ 'だ': ('た', 0xDE), 'ぢ': ('ち', 0xDE), 'づ': ('つ', 0xDE),
68
+ 'で': ('て', 0xDE), 'ど': ('と', 0xDE),
69
+ 'ば': ('は', 0xDE), 'び': ('ひ', 0xDE), 'ぶ': ('ふ', 0xDE),
70
+ 'べ': ('へ', 0xDE), 'ぼ': ('ほ', 0xDE),
71
+ 'ぱ': ('は', 0xDF), 'ぴ': ('ひ', 0xDF), 'ぷ': ('ふ', 0xDF),
72
+ 'ぺ': ('へ', 0xDF), 'ぽ': ('ほ', 0xDF),
73
+ 'ガ': ('カ', 0xDE), 'ギ': ('キ', 0xDE), 'グ': ('ク', 0xDE),
74
+ 'ゲ': ('ケ', 0xDE), 'ゴ': ('コ', 0xDE),
75
+ 'ザ': ('サ', 0xDE), 'ジ': ('シ', 0xDE), 'ズ': ('ス', 0xDE),
76
+ 'ゼ': ('セ', 0xDE), 'ゾ': ('ソ', 0xDE),
77
+ 'ダ': ('タ', 0xDE), 'ヂ': ('チ', 0xDE), 'ヅ': ('ツ', 0xDE),
78
+ 'デ': ('テ', 0xDE), 'ド': ('ト', 0xDE),
79
+ 'バ': ('ハ', 0xDE), 'ビ': ('ヒ', 0xDE), 'ブ': ('フ', 0xDE),
80
+ 'ベ': ('ヘ', 0xDE), 'ボ': ('ホ', 0xDE),
81
+ 'パ': ('ハ', 0xDF), 'ピ': ('ヒ', 0xDF), 'プ': ('フ', 0xDF),
82
+ 'ペ': ('ヘ', 0xDF), 'ポ': ('ホ', 0xDF)
83
+ }
84
+
85
+
86
+ class MSXJapaneseCodec(MSXCodecBase):
87
+ """Japanese MSX character set codec"""
88
+
89
+ def __init__(self):
90
+ super().__init__('msx-jp', MSX_CUSTOM_MAP_JP, GRAPHIC_CHAR_MAP_JP)
91
+ self.dakuten_map = DAKUTEN_MAP
92
+ self.dakuten_reverse_map = {(v, m): k for k, (v, m) in DAKUTEN_MAP.items()}
93
+
94
+ def encode(self, text, errors='strict'):
95
+ """Convert UTF-8 string to MSX code byte sequence (Japanese)"""
96
+ result = bytearray()
97
+ for char in text:
98
+ if char in self.graphic_map:
99
+ result.extend(self.graphic_map[char])
100
+ elif char in self.dakuten_map:
101
+ base, mark = self.dakuten_map[char]
102
+ result.extend([self.char_reverse_map.get(base, 0x3F), mark])
103
+ else:
104
+ result.append(self.char_reverse_map.get(char, 0x3F))
105
+ return bytes(result), len(text)
106
+
107
+ def decode(self, codes, errors='strict'):
108
+ """Convert MSX code byte sequence to UTF-8 string"""
109
+ result = []
110
+ i = 0
111
+ codes_len = len(codes)
112
+
113
+ while i < codes_len:
114
+ code = codes[i]
115
+
116
+ # Handle graphic characters
117
+ if code == 0x01 and i + 1 < codes_len:
118
+ next_code = codes[i + 1]
119
+ char = self.graphic_reverse_map.get((code, next_code))
120
+ if char:
121
+ result.append(char)
122
+ i += 2
123
+ continue
124
+
125
+ # Handle dakuten/handakuten
126
+ if i + 1 < codes_len and codes[i + 1] in (0xDE, 0xDF):
127
+ base_char = self.char_map.get(code)
128
+ mark = codes[i + 1]
129
+ composed = self.dakuten_reverse_map.get((base_char, mark))
130
+ if composed:
131
+ result.append(composed)
132
+ else:
133
+ mark_char = '゛' if mark == 0xDE else '゜'
134
+ result.append(base_char + mark_char)
135
+ i += 2
136
+ else:
137
+ result.append(self.char_map.get(code, '?'))
138
+ i += 1
139
+
140
+ return ''.join(result), len(codes)
141
+
142
+
143
+ # Create codec instance
144
+ msx_codec_jp = MSXJapaneseCodec().codec_info
@@ -0,0 +1 @@
1
+ # Test package initialization file
@@ -0,0 +1,70 @@
1
+ import unittest
2
+ import codecs
3
+ from msx_charset import msx_charset
4
+
5
+
6
+ class TestMSXCharsetBR(unittest.TestCase):
7
+ @classmethod
8
+ def setUpClass(cls):
9
+ # テスト実行前に文字セットを登録
10
+ codecs.register(msx_charset)
11
+
12
+ def test_encode_decode_ascii(self):
13
+ text = "Hello, World!"
14
+ encoded = text.encode("msx-br")
15
+ decoded = encoded.decode("msx-br")
16
+ self.assertEqual(text, decoded)
17
+
18
+ def test_encode_decode_accented_chars(self):
19
+ text = "Á¨ÍÓÚÂÊÔÀ"
20
+ encoded = text.encode("msx-br")
21
+ decoded = encoded.decode("msx-br")
22
+ self.assertEqual(text, decoded)
23
+
24
+ def test_encode_decode_graphic_chars(self):
25
+ text = "☺☻♥♦♣♠"
26
+ encoded = text.encode("msx-br")
27
+ decoded = encoded.decode("msx-br")
28
+ self.assertEqual(text, decoded)
29
+
30
+ def test_encode_decode_mixed(self):
31
+ text = "Olá, mundo!"
32
+ encoded = text.encode("msx-br")
33
+ decoded = encoded.decode("msx-br")
34
+ self.assertEqual(text, decoded)
35
+
36
+ def test_codec_registration(self):
37
+ self.assertIsNotNone(codecs.lookup("msx-br"))
38
+ self.assertIsNotNone(codecs.lookup("msx_br"))
39
+
40
+ def test_encode_bytes_ascii(self):
41
+ # ASCII文字のバイト列テスト
42
+ text = "Hello!"
43
+ expected = bytes([0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x21])
44
+ encoded = text.encode("msx-br")
45
+ self.assertEqual(encoded, expected)
46
+
47
+ def test_encode_bytes_accented(self):
48
+ # アクセント付き文字のバイト列テスト
49
+ text = "Á¨ÍÓÚÂÊÔÀ"
50
+ expected = bytes([0x84, 0x86, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F]) # MSXブラジル文字セットの対応するバイト値
51
+ encoded = text.encode("msx-br")
52
+ self.assertEqual(encoded, expected)
53
+
54
+ def test_encode_bytes_graphic(self):
55
+ # グラフィック文字のバイト列テスト
56
+ text = "☺☻"
57
+ expected = bytes([0x01, 0x41, 0x01, 0x42]) # MSXブラジル文字セットの対応するバイト値
58
+ encoded = text.encode("msx-br")
59
+ self.assertEqual(encoded, expected)
60
+
61
+ def test_encode_bytes_mixed(self):
62
+ # 混合文字のバイト列テスト
63
+ text = "Aá"
64
+ expected = bytes([0x41, 0xA0]) # MSXブラジル文字セットの対応するバイト値
65
+ encoded = text.encode("msx-br")
66
+ self.assertEqual(encoded, expected)
67
+
68
+
69
+ if __name__ == "__main__":
70
+ unittest.main()
@@ -0,0 +1,77 @@
1
+ import unittest
2
+ import codecs
3
+ from msx_charset import msx_charset
4
+
5
+
6
+
7
+ class TestMSXCharsetIntl(unittest.TestCase):
8
+ @classmethod
9
+ def setUpClass(cls):
10
+ # テスト実行前にコーデックを登録
11
+ codecs.register(msx_charset)
12
+
13
+ def test_encode_decode_ascii(self):
14
+ text = "Hello, World!"
15
+ encoded = text.encode("msx-intl")
16
+ decoded = encoded.decode("msx-intl")
17
+ self.assertEqual(text, decoded)
18
+
19
+ def test_encode_decode_accented_chars(self):
20
+ text = "áéíóúàèìòùâêîôû"
21
+ encoded = text.encode("msx-intl")
22
+ decoded = encoded.decode("msx-intl")
23
+ self.assertEqual(text, decoded)
24
+
25
+ def test_encode_decode_graphic_chars(self):
26
+ text = "☺☻♥♦♣♠"
27
+ encoded = text.encode("msx-intl")
28
+ decoded = encoded.decode("msx-intl")
29
+ self.assertEqual(text, decoded)
30
+
31
+ def test_encode_decode_mixed(self):
32
+ text = "Hello, áéíóú!"
33
+ encoded = text.encode("msx-intl")
34
+ decoded = encoded.decode("msx-intl")
35
+ self.assertEqual(text, decoded)
36
+
37
+ def test_codec_registration(self):
38
+ self.assertIsNotNone(codecs.lookup("msx-intl"))
39
+ self.assertIsNotNone(codecs.lookup("msx_intl"))
40
+
41
+ def test_encode_bytes_ascii(self):
42
+ # ASCII文字のバイト列テスト
43
+ text = "Hello!"
44
+ expected = bytes([0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x21])
45
+ encoded = text.encode("msx-intl")
46
+ self.assertEqual(encoded, expected)
47
+
48
+ def test_encode_bytes_accented(self):
49
+ # アクセント付き文字のバイト列テスト
50
+ text = "áé"
51
+ expected = bytes([0xA0, 0x82]) # MSXインターナショナル文字セットの対応するバイト値
52
+ encoded = text.encode("msx-intl")
53
+ self.assertEqual(encoded, expected)
54
+
55
+ def test_encode_bytes_graphic(self):
56
+ # グラフィック文字のバイト列テスト
57
+ text = "☺☻"
58
+ expected = bytes([0x01, 0x41, 0x01, 0x42]) # MSXインターナショナル文字セットの対応するバイト値
59
+ encoded = text.encode("msx-intl")
60
+ self.assertEqual(encoded, expected)
61
+
62
+ def test_encode_bytes_mixed(self):
63
+ # 混合文字のバイト列テスト
64
+ text = "Aá"
65
+ expected = bytes([0x41, 0xA0]) # MSXインターナショナル文字セットの対応するバイト値
66
+ encoded = text.encode("msx-intl")
67
+ self.assertEqual(encoded, expected)
68
+
69
+ def test_encode_decode_currency(self):
70
+ text = "£$¥"
71
+ encoded = text.encode("msx-intl")
72
+ decoded = encoded.decode("msx-intl")
73
+ self.assertEqual(text, decoded)
74
+
75
+
76
+ if __name__ == "__main__":
77
+ unittest.main()
@@ -0,0 +1,109 @@
1
+ import unittest
2
+ import codecs
3
+ from msx_charset import msx_charset
4
+
5
+
6
+ class TestMSXCharsetJP(unittest.TestCase):
7
+ @classmethod
8
+ def setUpClass(cls):
9
+ # テスト実行前にコーデックを登録
10
+ codecs.register(msx_charset)
11
+
12
+ def test_encode_decode_ascii(self):
13
+ text = "Hello, World!"
14
+ encoded = text.encode("msx-jp")
15
+ decoded = encoded.decode("msx-jp")
16
+ self.assertEqual(text, decoded)
17
+
18
+ def test_encode_decode_hiragana(self):
19
+ text = "こんにちは"
20
+ encoded = text.encode("msx-jp")
21
+ decoded = encoded.decode("msx-jp")
22
+ self.assertEqual(text, decoded)
23
+
24
+ def test_encode_decode_katakana(self):
25
+ text = "コンニチハ"
26
+ encoded = text.encode("msx-jp")
27
+ decoded = encoded.decode("msx-jp")
28
+ self.assertEqual(text, decoded)
29
+
30
+ def test_encode_decode_dakuten(self):
31
+ text = "がぎぐげご"
32
+ encoded = text.encode("msx-jp")
33
+ decoded = encoded.decode("msx-jp")
34
+ self.assertEqual(text, decoded)
35
+
36
+ def test_encode_decode_handakuten(self):
37
+ text = "ぱぴぷぺぽ"
38
+ encoded = text.encode("msx-jp")
39
+ decoded = encoded.decode("msx-jp")
40
+ self.assertEqual(text, decoded)
41
+
42
+ def test_encode_decode_graphic_chars(self):
43
+ text = "月火水木金土日"
44
+ encoded = text.encode("msx-jp")
45
+ decoded = encoded.decode("msx-jp")
46
+ self.assertEqual(text, decoded)
47
+
48
+ def test_encode_decode_mixed(self):
49
+ text = "Hello、こんにちは!"
50
+ encoded = text.encode("msx-jp")
51
+ decoded = encoded.decode("msx-jp")
52
+ self.assertEqual(text, decoded)
53
+
54
+ def test_codec_registration(self):
55
+ self.assertIsNotNone(codecs.lookup("msx-jp"))
56
+ self.assertIsNotNone(codecs.lookup("msx_jp"))
57
+
58
+ def test_encode_bytes_ascii(self):
59
+ # ASCII文字のバイト列テスト
60
+ text = "Hello!"
61
+ expected = bytes([0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x21])
62
+ encoded = text.encode("msx-jp")
63
+ self.assertEqual(encoded, expected)
64
+
65
+ def test_encode_bytes_hiragana(self):
66
+ # ひらがなのバイト列テスト
67
+ text = "こんにちは"
68
+ expected = bytes([0x9A, 0xFD, 0xE6, 0xE1, 0xEA])
69
+ encoded = text.encode("msx-jp")
70
+ self.assertEqual(encoded, expected)
71
+
72
+ def test_encode_bytes_katakana(self):
73
+ # カタカナのバイト列テスト
74
+ text = "コンニチハ"
75
+ expected = bytes([0xBA, 0xDD, 0xC6, 0xC1, 0xCA])
76
+ encoded = text.encode("msx-jp")
77
+ self.assertEqual(encoded, expected)
78
+
79
+ def test_encode_bytes_dakuten(self):
80
+ # 濁点文字のバイト列テスト
81
+ text = "がぎ"
82
+ expected = bytes([0x96, 0xDE, 0x97, 0xDE])
83
+ encoded = text.encode("msx-jp")
84
+ self.assertEqual(encoded, expected)
85
+
86
+ def test_encode_bytes_handakuten(self):
87
+ # 半濁点文字のバイト列テスト
88
+ text = "ぱぴ"
89
+ expected = bytes([0xEA, 0xDF, 0xEB, 0xDF])
90
+ encoded = text.encode("msx-jp")
91
+ self.assertEqual(encoded, expected)
92
+
93
+ def test_encode_bytes_graphic(self):
94
+ # グラフィック文字のバイト列テスト
95
+ text = "月火"
96
+ expected = bytes([0x01, 0x41, 0x01, 0x42])
97
+ encoded = text.encode("msx-jp")
98
+ self.assertEqual(encoded, expected)
99
+
100
+ def test_encode_bytes_mixed(self):
101
+ # 混合文字のバイト列テスト
102
+ text = "A、あ"
103
+ expected = bytes([0x41, 0xA4, 0x91])
104
+ encoded = text.encode("msx-jp")
105
+ self.assertEqual(encoded, expected)
106
+
107
+
108
+ if __name__ == "__main__":
109
+ unittest.main()
@@ -0,0 +1,14 @@
1
+ LICENSE
2
+ MANIFEST.in
3
+ README.md
4
+ pyproject.toml
5
+ setup.py
6
+ msx_charset/__init__.py
7
+ msx_charset/codec_base.py
8
+ msx_charset/codec_br.py
9
+ msx_charset/codec_intl.py
10
+ msx_charset/codec_jp.py
11
+ msx_charset/tests/__init__.py
12
+ msx_charset/tests/test_charset_br.py
13
+ msx_charset/tests/test_charset_intl.py
14
+ msx_charset/tests/test_charset_jp.py
@@ -0,0 +1,37 @@
1
+ [build-system]
2
+ requires = ["setuptools>=42", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "msx-charset"
7
+ version = "0.1.0"
8
+ authors = [
9
+ {name = "yamamo-to", email = "humorum@gmail.com"}
10
+ ]
11
+ description = "MSX character set codecs for Python"
12
+ readme = "README.md"
13
+ requires-python = ">=3.7"
14
+ classifiers = [
15
+ "Development Status :: 4 - Beta",
16
+ "Intended Audience :: Developers",
17
+ "Programming Language :: Python :: 3",
18
+ "Programming Language :: Python :: 3.8",
19
+ "Programming Language :: Python :: 3.9",
20
+ "Programming Language :: Python :: 3.10",
21
+ "Programming Language :: Python :: 3.11",
22
+ "Topic :: Software Development :: Libraries :: Python Modules",
23
+ ]
24
+ license = "MIT"
25
+
26
+ [tool.setuptools]
27
+ packages = ["msx_charset"]
28
+ license-files = []
29
+
30
+ [tool.pytest.ini_options]
31
+ testpaths = ["msx_charset/tests"]
32
+ python_files = ["test_*.py"]
33
+
34
+ [project.urls]
35
+ Repository = "https://github.com/yamamo-to/msx-charset"
36
+ Issue = "https://github.com/yamamo-to/msx-charset/issues"
37
+
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,24 @@
1
+ from setuptools import setup, find_packages
2
+
3
+ setup(
4
+ name="msx-charset",
5
+ version="0.1.0",
6
+ packages=find_packages(),
7
+ install_requires=[],
8
+ python_requires=">=3.8",
9
+ author="yamamo-to",
10
+ author_email="humorum@gmail.com",
11
+ description="MSX Japanese, International, and Brazilian character codec for Python",
12
+ long_description=open("README.md", encoding='utf-8').read(),
13
+ long_description_content_type="text/markdown",
14
+ url="https://github.com/yamamo-to/msx-charset",
15
+ classifiers=[
16
+ "Programming Language :: Python :: 3",
17
+ "Programming Language :: Python :: 3.8",
18
+ "Programming Language :: Python :: 3.9",
19
+ "Programming Language :: Python :: 3.10",
20
+ "Programming Language :: Python :: 3.11",
21
+ "Programming Language :: Python :: 3.12",
22
+ "Operating System :: OS Independent",
23
+ ],
24
+ )