quotes-convert 1.0.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,4 @@
1
+ # These are supported funding model platforms
2
+
3
+ github: ysskrishna
4
+ patreon: ysskrishna
@@ -0,0 +1,34 @@
1
+ name: "Publish"
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ # Publish on any tag starting with a `v`, e.g., v0.1.0
7
+ - v*
8
+
9
+ jobs:
10
+ run:
11
+ runs-on: ubuntu-latest
12
+ environment:
13
+ name: pypi
14
+ permissions:
15
+ id-token: write
16
+ contents: write
17
+ steps:
18
+ - name: Checkout
19
+ uses: actions/checkout@v5
20
+ - name: Install uv
21
+ uses: astral-sh/setup-uv@v6
22
+ - name: Install Python 3.8
23
+ run: uv python install 3.8
24
+ - name: Build
25
+ run: uv build
26
+ - name: Publish
27
+ run: uv publish
28
+ - name: Create GitHub Release
29
+ uses: softprops/action-gh-release@v2
30
+ with:
31
+ tag_name: ${{ github.ref_name }}
32
+ name: Release ${{ github.ref_name }}
33
+ env:
34
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -0,0 +1,33 @@
1
+ name: Tests
2
+
3
+ on:
4
+ push:
5
+ branches: [ main ]
6
+ pull_request:
7
+ branches: [ main ]
8
+
9
+ jobs:
10
+ test:
11
+ runs-on: ubuntu-latest
12
+ strategy:
13
+ matrix:
14
+ python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"]
15
+
16
+ steps:
17
+ - uses: actions/checkout@v4
18
+
19
+ - name: Set up Python ${{ matrix.python-version }}
20
+ uses: actions/setup-python@v5
21
+ with:
22
+ python-version: ${{ matrix.python-version }}
23
+
24
+ - name: Install uv
25
+ uses: astral-sh/setup-uv@v4
26
+ with:
27
+ version: "latest"
28
+
29
+ - name: Install dependencies
30
+ run: uv sync --dev
31
+
32
+ - name: Run tests
33
+ run: uv run pytest tests/ -v
@@ -0,0 +1,11 @@
1
+ # Python-generated files
2
+ __pycache__/
3
+ *.py[oc]
4
+ build/
5
+ dist/
6
+ wheels/
7
+ *.egg-info
8
+
9
+ # Virtual environments
10
+ .venv
11
+ workspace/
@@ -0,0 +1 @@
1
+ 3.8
@@ -0,0 +1,30 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [1.0.0]
9
+
10
+ ### Added
11
+ - Initial release of quotes-convert Python package
12
+ - `single_quotes()` function to convert strings to use single quotes
13
+ - `double_quotes()` function to convert strings to use double quotes
14
+ - `single_quotes_stream()` function for streaming conversion to single quotes
15
+ - `double_quotes_stream()` function for streaming conversion to double quotes
16
+ - Stateful quote converter with proper escaping and unescaping
17
+ - Support for mixed quote types within strings
18
+ - Handles escaped quotes and backslashes correctly
19
+ - Type hints for better IDE support and type checking
20
+ - Zero dependencies for minimal overhead
21
+ - Comprehensive test suite with 59 tests covering edge cases
22
+ - Python 3.8+ compatibility
23
+
24
+ ### Features
25
+ - Automatically converts between single and double quotes
26
+ - Properly escapes and unescapes quotes during conversion
27
+ - Stream processing support for large text processing
28
+ - Handles complex escaping scenarios
29
+
30
+ [1.0.0]: https://github.com/ysskrishna/quotes-convert/releases/tag/v1.0.0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Y. Siva Sai Krishna
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,180 @@
1
+ Metadata-Version: 2.4
2
+ Name: quotes-convert
3
+ Version: 1.0.0
4
+ Summary: Convert matching double-quotes to single-quotes or vice versa in strings and streams. Inspired by the popular to-single-quotes npm package
5
+ Project-URL: Homepage, https://github.com/ysskrishna/quotes-convert
6
+ Project-URL: Repository, https://github.com/ysskrishna/quotes-convert.git
7
+ Project-URL: Issues, https://github.com/ysskrishna/quotes-convert/issues
8
+ Project-URL: Changelog, https://github.com/ysskrishna/quotes-convert/blob/main/CHANGELOG.md
9
+ Author-email: "Y. Siva Sai Krishna" <sivasaikrishnassk@gmail.com>
10
+ License: MIT
11
+ License-File: LICENSE
12
+ Keywords: convert,double-quotes,escaping,quotes,single-quotes,strings,text-processing,utilities
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Operating System :: OS Independent
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.8
19
+ Classifier: Programming Language :: Python :: 3.9
20
+ Classifier: Programming Language :: Python :: 3.10
21
+ Classifier: Programming Language :: Python :: 3.11
22
+ Classifier: Programming Language :: Python :: 3.12
23
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
24
+ Classifier: Topic :: Text Processing
25
+ Classifier: Topic :: Utilities
26
+ Classifier: Typing :: Typed
27
+ Requires-Python: >=3.8
28
+ Description-Content-Type: text/markdown
29
+
30
+ # quotes-convert
31
+
32
+ [![Python Version](https://img.shields.io/badge/python-3.8%2B-blue.svg)](https://www.python.org/downloads/)
33
+ [![License](https://img.shields.io/badge/license-MIT-green.svg)](https://github.com/ysskrishna/quotes-convert/blob/main/LICENSE)
34
+ ![Tests](https://github.com/ysskrishna/quotes-convert/actions/workflows/test.yml/badge.svg)
35
+ [![PyPI](https://img.shields.io/pypi/v/quotes-convert)](https://pypi.org/project/quotes-convert/)
36
+ [![PyPI Downloads](https://static.pepy.tech/personalized-badge/quotes-convert?period=total&units=INTERNATIONAL_SYSTEM&left_color=GREY&right_color=BLUE&left_text=downloads)](https://pepy.tech/projects/quotes-convert)
37
+
38
+ Convert matching double-quotes to single-quotes or vice versa in strings and streams. Inspired by the popular [to-single-quotes](https://github.com/sindresorhus/to-single-quotes) npm package.
39
+
40
+ ## Features
41
+
42
+ - **Multiple input types**: Convert quotes in strings and streams
43
+ - **Proper escaping**: Automatically handles quote escaping and unescaping
44
+ - **Memory efficient**: Process large texts with streaming without loading everything into memory
45
+ - **Zero dependencies**: Lightweight with no external dependencies
46
+ - **Type safe**: Full type hints for excellent IDE support
47
+
48
+ ## Why use this library?
49
+
50
+ Why not just use `.replace('"', "'")`? Because simply replacing quotes breaks strings that contain escaped quotes.
51
+
52
+ ```python
53
+ # The problem with simple replace
54
+ original = r'He said "Don\'t do it"'
55
+ broken = original.replace('"', "'")
56
+ # Result: 'He said 'Don\'t do it'' -> Syntax Error!
57
+
58
+ # The solution: quotes-convert handles escaping correctly
59
+ from quotes_convert import single_quotes
60
+ fixed = single_quotes(original)
61
+ # Result: 'He said "Don\'t do it"' -> Correctly preserves meaning
62
+ ```
63
+
64
+ ## Installation
65
+
66
+ ```bash
67
+ pip install quotes-convert
68
+ ```
69
+
70
+ ## Usage Examples
71
+
72
+ ### Basic Usage
73
+
74
+ ```python
75
+ from quotes_convert import single_quotes, double_quotes
76
+
77
+ result = single_quotes('x = "hello"; y = "world"')
78
+ print(result) # x = 'hello'; y = 'world'
79
+
80
+
81
+ result = double_quotes('x = "hello"; y = "world"')
82
+ print(result) # x = "hello"; y = "world"
83
+ ```
84
+
85
+ ### Handling Mixed Quotes
86
+
87
+ ```python
88
+ from quotes_convert import single_quotes, double_quotes
89
+
90
+ # Automatically escapes inner quotes
91
+ result = single_quotes('"it\'s working"')
92
+ print(result) # 'it\'s working'
93
+
94
+ result = double_quotes("'say \"hi\"'")
95
+ print(result) # "say \"hi\""
96
+ ```
97
+
98
+ ### Processing JSON-like Strings
99
+
100
+ Useful for normalizing JSON strings or Python dict definitions.
101
+
102
+ ```python
103
+ from quotes_convert import double_quotes
104
+
105
+ json_str = "{'key': 'value', 'nested': {'inner': 'data'}}"
106
+ result = double_quotes(json_str) # {"key": "value", "nested": {"inner": "data"}}
107
+ ```
108
+
109
+ ### Shell Script Processing
110
+
111
+ ```python
112
+ from quotes_convert import single_quotes
113
+
114
+ script = 'echo "Hello $USER"; grep "pattern" file.txt'
115
+ result = single_quotes(script) # echo 'Hello $USER'; grep 'pattern' file.txt
116
+ ```
117
+
118
+ ## Streaming Large Texts
119
+
120
+ Process large files or streams efficiently without loading the entire content into memory.
121
+
122
+ ```python
123
+ from quotes_convert import single_quotes_stream
124
+
125
+ def line_generator():
126
+ yield 'line 1: "hello"\n'
127
+ yield 'line 2: "world"\n'
128
+
129
+ # Process the stream chunk by chunk
130
+ for chunk in single_quotes_stream(line_generator()):
131
+ print(chunk, end='')
132
+
133
+ # Output:
134
+ # line 1: 'hello'
135
+ # line 2: 'world'
136
+ ```
137
+
138
+ ## API Reference
139
+
140
+ | Function | Description |
141
+ |----------|-------------|
142
+ | `single_quotes(text: str) -> str` | Convert matching double-quotes to single-quotes. |
143
+ | `double_quotes(text: str) -> str` | Convert matching single-quotes to double-quotes. |
144
+ | `single_quotes_stream(stream: Iterable[str]) -> Generator[str, None, None]` | Convert matching double-quotes to single-quotes in a stream, yielding chunks. |
145
+ | `double_quotes_stream(stream: Iterable[str]) -> Generator[str, None, None]` | Convert matching single-quotes to double-quotes in a stream, yielding chunks. |
146
+
147
+
148
+ ## Acknowledgments
149
+
150
+ Inspired by [Sindre Sorhus](https://github.com/sindresorhus)'s [to-single-quotes](https://github.com/sindresorhus/to-single-quotes) npm package.
151
+
152
+ ## Changelog
153
+
154
+ See [CHANGELOG.md](https://github.com/ysskrishna/quotes-convert/blob/main/CHANGELOG.md) for a detailed list of changes and version history.
155
+
156
+ ## Contributing
157
+
158
+ We welcome contributions! Please see our [Contributing Guide](https://github.com/ysskrishna/quotes-convert/blob/main/CONTRIBUTING.md) for details.
159
+
160
+ ## Support
161
+
162
+ If you find this library helpful:
163
+
164
+ - ⭐ Star the repository
165
+ - 🐛 Report issues
166
+ - 🔀 Submit pull requests
167
+ - 💝 [Sponsor on GitHub](https://github.com/sponsors/ysskrishna)
168
+
169
+ ## License
170
+
171
+ MIT © [Y. Siva Sai Krishna](https://github.com/ysskrishna) - see [LICENSE](https://github.com/ysskrishna/quotes-convert/blob/main/LICENSE) file for details.
172
+
173
+ ---
174
+
175
+ <p align="left">
176
+ <a href="https://github.com/ysskrishna">Author's GitHub</a> •
177
+ <a href="https://linkedin.com/in/ysskrishna">Author's LinkedIn</a> •
178
+ <a href="https://github.com/ysskrishna/quotes-convert/issues">Report Issues</a> •
179
+ <a href="https://pypi.org/project/quotes-convert/">Package on PyPI</a>
180
+ </p>
@@ -0,0 +1,151 @@
1
+ # quotes-convert
2
+
3
+ [![Python Version](https://img.shields.io/badge/python-3.8%2B-blue.svg)](https://www.python.org/downloads/)
4
+ [![License](https://img.shields.io/badge/license-MIT-green.svg)](https://github.com/ysskrishna/quotes-convert/blob/main/LICENSE)
5
+ ![Tests](https://github.com/ysskrishna/quotes-convert/actions/workflows/test.yml/badge.svg)
6
+ [![PyPI](https://img.shields.io/pypi/v/quotes-convert)](https://pypi.org/project/quotes-convert/)
7
+ [![PyPI Downloads](https://static.pepy.tech/personalized-badge/quotes-convert?period=total&units=INTERNATIONAL_SYSTEM&left_color=GREY&right_color=BLUE&left_text=downloads)](https://pepy.tech/projects/quotes-convert)
8
+
9
+ Convert matching double-quotes to single-quotes or vice versa in strings and streams. Inspired by the popular [to-single-quotes](https://github.com/sindresorhus/to-single-quotes) npm package.
10
+
11
+ ## Features
12
+
13
+ - **Multiple input types**: Convert quotes in strings and streams
14
+ - **Proper escaping**: Automatically handles quote escaping and unescaping
15
+ - **Memory efficient**: Process large texts with streaming without loading everything into memory
16
+ - **Zero dependencies**: Lightweight with no external dependencies
17
+ - **Type safe**: Full type hints for excellent IDE support
18
+
19
+ ## Why use this library?
20
+
21
+ Why not just use `.replace('"', "'")`? Because simply replacing quotes breaks strings that contain escaped quotes.
22
+
23
+ ```python
24
+ # The problem with simple replace
25
+ original = r'He said "Don\'t do it"'
26
+ broken = original.replace('"', "'")
27
+ # Result: 'He said 'Don\'t do it'' -> Syntax Error!
28
+
29
+ # The solution: quotes-convert handles escaping correctly
30
+ from quotes_convert import single_quotes
31
+ fixed = single_quotes(original)
32
+ # Result: 'He said "Don\'t do it"' -> Correctly preserves meaning
33
+ ```
34
+
35
+ ## Installation
36
+
37
+ ```bash
38
+ pip install quotes-convert
39
+ ```
40
+
41
+ ## Usage Examples
42
+
43
+ ### Basic Usage
44
+
45
+ ```python
46
+ from quotes_convert import single_quotes, double_quotes
47
+
48
+ result = single_quotes('x = "hello"; y = "world"')
49
+ print(result) # x = 'hello'; y = 'world'
50
+
51
+
52
+ result = double_quotes('x = "hello"; y = "world"')
53
+ print(result) # x = "hello"; y = "world"
54
+ ```
55
+
56
+ ### Handling Mixed Quotes
57
+
58
+ ```python
59
+ from quotes_convert import single_quotes, double_quotes
60
+
61
+ # Automatically escapes inner quotes
62
+ result = single_quotes('"it\'s working"')
63
+ print(result) # 'it\'s working'
64
+
65
+ result = double_quotes("'say \"hi\"'")
66
+ print(result) # "say \"hi\""
67
+ ```
68
+
69
+ ### Processing JSON-like Strings
70
+
71
+ Useful for normalizing JSON strings or Python dict definitions.
72
+
73
+ ```python
74
+ from quotes_convert import double_quotes
75
+
76
+ json_str = "{'key': 'value', 'nested': {'inner': 'data'}}"
77
+ result = double_quotes(json_str) # {"key": "value", "nested": {"inner": "data"}}
78
+ ```
79
+
80
+ ### Shell Script Processing
81
+
82
+ ```python
83
+ from quotes_convert import single_quotes
84
+
85
+ script = 'echo "Hello $USER"; grep "pattern" file.txt'
86
+ result = single_quotes(script) # echo 'Hello $USER'; grep 'pattern' file.txt
87
+ ```
88
+
89
+ ## Streaming Large Texts
90
+
91
+ Process large files or streams efficiently without loading the entire content into memory.
92
+
93
+ ```python
94
+ from quotes_convert import single_quotes_stream
95
+
96
+ def line_generator():
97
+ yield 'line 1: "hello"\n'
98
+ yield 'line 2: "world"\n'
99
+
100
+ # Process the stream chunk by chunk
101
+ for chunk in single_quotes_stream(line_generator()):
102
+ print(chunk, end='')
103
+
104
+ # Output:
105
+ # line 1: 'hello'
106
+ # line 2: 'world'
107
+ ```
108
+
109
+ ## API Reference
110
+
111
+ | Function | Description |
112
+ |----------|-------------|
113
+ | `single_quotes(text: str) -> str` | Convert matching double-quotes to single-quotes. |
114
+ | `double_quotes(text: str) -> str` | Convert matching single-quotes to double-quotes. |
115
+ | `single_quotes_stream(stream: Iterable[str]) -> Generator[str, None, None]` | Convert matching double-quotes to single-quotes in a stream, yielding chunks. |
116
+ | `double_quotes_stream(stream: Iterable[str]) -> Generator[str, None, None]` | Convert matching single-quotes to double-quotes in a stream, yielding chunks. |
117
+
118
+
119
+ ## Acknowledgments
120
+
121
+ Inspired by [Sindre Sorhus](https://github.com/sindresorhus)'s [to-single-quotes](https://github.com/sindresorhus/to-single-quotes) npm package.
122
+
123
+ ## Changelog
124
+
125
+ See [CHANGELOG.md](https://github.com/ysskrishna/quotes-convert/blob/main/CHANGELOG.md) for a detailed list of changes and version history.
126
+
127
+ ## Contributing
128
+
129
+ We welcome contributions! Please see our [Contributing Guide](https://github.com/ysskrishna/quotes-convert/blob/main/CONTRIBUTING.md) for details.
130
+
131
+ ## Support
132
+
133
+ If you find this library helpful:
134
+
135
+ - ⭐ Star the repository
136
+ - 🐛 Report issues
137
+ - 🔀 Submit pull requests
138
+ - 💝 [Sponsor on GitHub](https://github.com/sponsors/ysskrishna)
139
+
140
+ ## License
141
+
142
+ MIT © [Y. Siva Sai Krishna](https://github.com/ysskrishna) - see [LICENSE](https://github.com/ysskrishna/quotes-convert/blob/main/LICENSE) file for details.
143
+
144
+ ---
145
+
146
+ <p align="left">
147
+ <a href="https://github.com/ysskrishna">Author's GitHub</a> •
148
+ <a href="https://linkedin.com/in/ysskrishna">Author's LinkedIn</a> •
149
+ <a href="https://github.com/ysskrishna/quotes-convert/issues">Report Issues</a> •
150
+ <a href="https://pypi.org/project/quotes-convert/">Package on PyPI</a>
151
+ </p>
@@ -0,0 +1,55 @@
1
+ [project]
2
+ name = "quotes-convert"
3
+ version = "1.0.0"
4
+ description = "Convert matching double-quotes to single-quotes or vice versa in strings and streams. Inspired by the popular to-single-quotes npm package"
5
+ readme = "README.md"
6
+ requires-python = ">=3.8"
7
+ authors = [
8
+ {name = "Y. Siva Sai Krishna", email = "sivasaikrishnassk@gmail.com"}
9
+ ]
10
+ license = {text = "MIT"}
11
+ classifiers = [
12
+ "Programming Language :: Python :: 3",
13
+ "Programming Language :: Python :: 3.8",
14
+ "Programming Language :: Python :: 3.9",
15
+ "Programming Language :: Python :: 3.10",
16
+ "Programming Language :: Python :: 3.11",
17
+ "Programming Language :: Python :: 3.12",
18
+ "License :: OSI Approved :: MIT License",
19
+ "Operating System :: OS Independent",
20
+ "Development Status :: 4 - Beta",
21
+ "Intended Audience :: Developers",
22
+ "Topic :: Software Development :: Libraries :: Python Modules",
23
+ "Topic :: Text Processing",
24
+ "Topic :: Utilities",
25
+ "Typing :: Typed",
26
+ ]
27
+ keywords = [
28
+ "quotes",
29
+ "convert",
30
+ "single-quotes",
31
+ "double-quotes",
32
+ "escaping",
33
+ "strings",
34
+ "text-processing",
35
+ "utilities",
36
+ ]
37
+ dependencies = []
38
+
39
+ [dependency-groups]
40
+ dev = [
41
+ "pytest>=8.3.5",
42
+ ]
43
+
44
+ [project.urls]
45
+ Homepage = "https://github.com/ysskrishna/quotes-convert"
46
+ Repository = "https://github.com/ysskrishna/quotes-convert.git"
47
+ Issues = "https://github.com/ysskrishna/quotes-convert/issues"
48
+ Changelog = "https://github.com/ysskrishna/quotes-convert/blob/main/CHANGELOG.md"
49
+
50
+ [build-system]
51
+ requires = ["hatchling"]
52
+ build-backend = "hatchling.build"
53
+
54
+ [tool.hatch.build.targets.wheel]
55
+ packages = ["quotes_convert"]
@@ -0,0 +1,13 @@
1
+ from .core import (
2
+ single_quotes,
3
+ double_quotes,
4
+ single_quotes_stream,
5
+ double_quotes_stream,
6
+ )
7
+
8
+ __all__ = [
9
+ "single_quotes",
10
+ "double_quotes",
11
+ "single_quotes_stream",
12
+ "double_quotes_stream",
13
+ ]
@@ -0,0 +1,89 @@
1
+ from typing import Optional, Tuple
2
+ from quotes_convert.model import SINGLE_QUOTE, DOUBLE_QUOTE, BACKSLASH, QuotePolicy
3
+
4
+
5
+ class QuoteConverter:
6
+ """State machine for converting quotes character by character.
7
+
8
+ This converter handles the stateful conversion of quotes, including
9
+ proper escaping and unescaping of quote characters.
10
+ """
11
+
12
+ def __init__(self, policy: QuotePolicy):
13
+ self.target = policy.target
14
+ self.source = policy.source
15
+
16
+ self.is_between_quotes = False
17
+ self.quote_character: Optional[str] = None
18
+ self._pending: Optional[str] = None
19
+
20
+ def feed(self, char: str) -> str:
21
+ """Feed a single character to the converter.
22
+
23
+ Args:
24
+ char: A single character to process
25
+
26
+ Returns:
27
+ Output string (may be empty if buffering)
28
+ """
29
+ if self._pending is None:
30
+ self._pending = char
31
+ return ""
32
+
33
+ current = self._pending
34
+ next_char = char
35
+
36
+ out, consumed = self._process(current, next_char)
37
+ self._pending = None if consumed else next_char
38
+ return out
39
+
40
+ def flush(self) -> str:
41
+ """Flush any pending character.
42
+
43
+ Returns:
44
+ Final output string
45
+ """
46
+ if self._pending is None:
47
+ return ""
48
+
49
+ ch = self._pending
50
+ self._pending = None
51
+ out, _ = self._process(ch, None)
52
+ return out
53
+
54
+ def _process(
55
+ self, current: str, next_char: Optional[str]
56
+ ) -> Tuple[str, bool]:
57
+
58
+ if current in (SINGLE_QUOTE, DOUBLE_QUOTE):
59
+ if not self.is_between_quotes:
60
+ self.quote_character = current
61
+ self.is_between_quotes = True
62
+ return self.target, False
63
+
64
+ if self.quote_character == current:
65
+ self.is_between_quotes = False
66
+ return self.target, False
67
+
68
+ return BACKSLASH + self.target, False
69
+
70
+ if current == BACKSLASH and next_char in (SINGLE_QUOTE, DOUBLE_QUOTE):
71
+ if next_char == self.target:
72
+ if (
73
+ self.is_between_quotes
74
+ and self.quote_character == self.target
75
+ ):
76
+ return BACKSLASH + self.target, True
77
+
78
+ return BACKSLASH + BACKSLASH + self.target, True
79
+
80
+ if next_char == self.source:
81
+ return (
82
+ self.source if self.is_between_quotes else self.target,
83
+ True,
84
+ )
85
+
86
+ if current == BACKSLASH and next_char == BACKSLASH:
87
+ return BACKSLASH + BACKSLASH, True
88
+
89
+ return current, False