{"id":28256022,"url":"https://github.com/playfulsparkle/pyprintf","last_synced_at":"2026-03-02T17:36:39.032Z","repository":{"id":288155428,"uuid":"967021090","full_name":"playfulsparkle/pyprintf","owner":"playfulsparkle","description":"A lightweight, Open Source pyprintf sprintf() implementation written in Python","archived":false,"fork":false,"pushed_at":"2025-10-24T16:23:44.000Z","size":97,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-10-24T18:27:19.536Z","etag":null,"topics":["formating","parser","python","sprintf","vsprintf"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/playfulsparkle.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-04-15T20:00:38.000Z","updated_at":"2025-10-24T16:23:47.000Z","dependencies_parsed_at":null,"dependency_job_id":"84794351-8c02-4f6f-abde-a71db3ed5bfe","html_url":"https://github.com/playfulsparkle/pyprintf","commit_stats":null,"previous_names":["playfulsparkle/pyprintf"],"tags_count":7,"template":false,"template_full_name":null,"purl":"pkg:github/playfulsparkle/pyprintf","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/playfulsparkle%2Fpyprintf","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/playfulsparkle%2Fpyprintf/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/playfulsparkle%2Fpyprintf/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/playfulsparkle%2Fpyprintf/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/playfulsparkle","download_url":"https://codeload.github.com/playfulsparkle/pyprintf/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/playfulsparkle%2Fpyprintf/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30012001,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-02T17:00:27.440Z","status":"ssl_error","status_checked_at":"2026-03-02T17:00:03.402Z","response_time":60,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["formating","parser","python","sprintf","vsprintf"],"created_at":"2025-05-19T22:15:16.501Z","updated_at":"2026-03-02T17:36:39.016Z","avatar_url":"https://github.com/playfulsparkle.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# pyprintf: Lightweight Python String Formatting Library\n\n[![PyPI Version](https://img.shields.io/pypi/v/pyprintf.svg)](https://pypi.org/project/pyprintf/)\n[![Python Versions](https://img.shields.io/pypi/pyversions/pyprintf.svg)](https://pypi.org/project/pyprintf/)\n[![License](https://img.shields.io/badge/License-BSD_3--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause)\n\nA **lightweight** and **open-source Python package** providing a robust **sprintf implementation** for **type-safe string formatting**. This library offers a familiar syntax for developers accustomed to `sprintf` from C and PHP, with enhanced Pythonic features including:\n\n- **Positional \u0026 Named Placeholders**\n- **Cross-version Compatibility** (Python 3.11+)\n- **Strict Type Validation**\n- **JSON Serialization Support**\n- **Configurable Formatting Rules**\n\n## Usage\n\nHere's a quick example to get you started:\n\n```python\nfrom pyprintf import sprintf\n\nname = \"World\"\ncount = 42\n\n# Simple positional formatting\ngreeting = sprintf(\"Hello %s!\", name)\nprint(greeting)  # Output: Hello World!\n\n# Formatting with a number\nmessage = sprintf(\"The answer is %d.\", count)\nprint(message)  # Output: The answer is 42.\n```\n\n## Installation\n\n### PyPi\n\n```bash\npip install pyprintf\n```\n\n## API\n\n### `sprintf`\n\nThe primary formatting function that mimics C-style `sprintf` behavior with Python enhancements.\n\n**Parameters:**\n- `format` (str): Format string containing text and placeholders\n- `*args` (Any): Variable arguments to format (positional or keyword)\n\n**Returns:**\n- `str`: Formatted string according to specifiers\n\n**Signature:**\n```python\ndef sprintf(format: str, *args: Any) -\u003e str:\n```\n\n**Example:**\n```python\nfrom pyprintf import sprintf\n\n# Positional arguments\noutput = sprintf(\"%s has %d apples\", \"Alice\", 5)\n# \"Alice has 5 apples\"\n\n# Named arguments\noutput = sprintf(\"Hello %(name)s!\", {\"name\": \"Bob\"})\n# \"Hello Bob!\"\n```\n\n### `vsprintf`\n\nArray-accepting variant of `sprintf` for pre-collected arguments.\n\n***Parameters:***\n\n- `format` (str): Format string containing text and placeholders\n- `*args` (Any): Variable arguments to format (positional or keyword)\n\n***Return Value:***\n\n- `str`: Formatted string according to specifiers\n\n**Signature:**\n```python\ndef vsprintf(format: str, *args: Any) -\u003e str:\n```\n\n### Difference between `sprintf` and `vsprintf`\n\nThe main difference is how they receive the values to be formatted: `sprintf` takes them as individual arguments after the format string, while `vsprintf` takes them as a single iterable (like a list or tuple) argument. `vsprintf` is useful when the arguments are already collected in a data structure.\n\n## Format String Placeholders\n\nThe `sprintf` function uses placeholders within the format string (the first argument) to indicate where and how subsequent arguments should be inserted and formatted. Placeholders begin with a `%` character and are followed by a sequence of optional formatting options and a required type specifier. This powerful system allows for precise control over the output format of your strings.\n\n### Optional Formatting Elements\n\nThe `sprintf` function supports a wide range of formatting options. Each placeholder can include optional modifiers to control how the corresponding value is displayed. Below are the supported options in the order they can appear in a format specifier:\n\n**Argument Index (Positional Specifier)**\n\n* **Syntax:** `%\u003cindex\u003e$\u003cspecifier\u003e` (e.g., `%2$s`)\n* **Description:** Specifies which argument to insert at this position, starting from `1`. Useful when you want to change the order of values.\n* **Default:** If omitted, arguments are used in the order they appear.\n* **Example:**\n\n```python\nsprintf(\"%2$s, %1$s!\", \"Hello\", \"World\") # will output \"World, Hello!\"\n```\n\n**Sign Indicator**\n\n* **Syntax:** `%+\u003cspecifier\u003e` (e.g., `%+d`)\n* **Description:** Forces numeric output to always include a sign (`+` or `-`).\n* **Default:** Only negative numbers show a sign.\n* **Example:**\n\n```python\nsprintf(\"%+d\", 5)  # will output \"+5\"\nsprintf(\"%+d\", -5) # will output \"-5\"\n```\n\n**Padding Specifier**\n\n* Syntax: `%0\u003cwidth\u003e\u003cspecifier\u003e` or `%'\u003cchar\u003e\u003cwidth\u003e\u003cspecifier\u003e` (e.g., `%05d`, `%'*5s`)\n* Description: Defines the character used for padding.\n    * `0` pads numeric types with leading zeros.\n    * `'` followed by a character pads with that character.\n* Default padding is with spaces.\n* Examples:\n\n```python\nsprintf(\"%05d\", 12)     # will output \"00012\"\nsprintf(\"%'*5s\", \"abc\") # will output \"**abc\"\n```\n\n**Alignment**\n\n* **Syntax:** `%-\u003cwidth\u003e\u003cspecifier\u003e` (e.g., `%-10s`)\n* **Description:** Aligns the output within the field width.\n* `-` aligns left.\n* Default is right alignment.\n* **Example:**\n\n```python\nsprintf(\"%-10s\", \"hello\") # will output \"hello     \"\nsprintf(\"%10s\", \"hello\")  # will output \"     hello\"\n```\n\n**Width**\n\n* **Syntax:** `%\u003cnumber\u003e\u003cspecifier\u003e` (e.g., `%10s`, `%5j`)\n* **Description:** Sets the minimum width of the output. Pads if the actual output is shorter.\n    * For the `j` (JSON) specifier, this defines the number of spaces used for indentation.\n* **Examples:**\n\n```python\nsprintf(\"%10s\", \"test\")  # will output \"      test\"\nsprintf(\"%5j\", { a: 1 }) # will output \"{\\n     \\\"a\\\": 1\\n}\"\n```\n\nPrecision\n\n**Syntax:** `%.\u003cnumber\u003e\u003cspecifier\u003e` (e.g., `%.2f`, `%.5g`, `%.10s`)\n**Description:** Controls output precision, depending on the type:\n* `f`, `e`: Number of digits after the decimal point.\n* `g`: Total significant digits.\n* `s`, `t`, `T`, `v`: Max characters (string is truncated).\n**Examples:**\n\n```python\nsprintf(\"%.2f\", 3.14159)             # will output \"3.14\"\nsprintf(\"%.5g\", 123.45678)           # will output \"123.46\"\nsprintf(\"%.5s\", \"This is long text\") # will output \"This \"\n```\n\n### Required Type Specifier\n\nThis single character at the end of the placeholder determines how the corresponding argument will be interpreted and formatted.\n\n| Specifier | Description                                                  | Python Example                            | Output               |\n| --------- | ------------------------------------------------------------ | ----------------------------------------- | -------------------- |\n| `%%`      | Outputs a literal percent sign                               | `sprintf(\"%%\")`                           | `%`                  |\n| `b`       | Integer in binary format                                     | `sprintf(\"%b\", 10)`                       | `1010`               |\n| `c`       | Integer as Unicode character                                 | `sprintf(\"%c\", 65)`                       | `A`                  |\n| `d`/`i`   | Signed decimal integer                                       | `sprintf(\"%d\", 123)`                      | `123`                |\n| `e`       | Floating point in scientific notation (lowercase \"e\")        | `sprintf(\"%e\", 123.45)`                   | `1.234500e+02`       |\n| `E`       | Floating point in scientific notation (uppercase \"E\")        | `sprintf(\"%E\", 123.45)`                   | `1.234500E+02`       |\n| `f`       | Floating point with decimal precision                        | `sprintf(\"%.2f\", 3.14159)`                | `3.14`               |\n| `g`       | Adaptive float formatting                                    | `sprintf(\"%.3g\", 1234.56)`                | `1.23e+03`           |\n| `o`       | Integer in octal format                                      | `sprintf(\"%o\", 10)`                       | `12`                 |\n| `s`       | String output                                                | `sprintf(\"%s\", \"hello\")`                  | `hello`              |\n| `t`       | Boolean (`\"True\"`/`\"False\"` capitalized strings)             | `sprintf(\"%t\", True)`                     | `True`               |\n| `T`       | Python type name (`\"list\"`/`\"NoneType\"` capitalized strings) | `sprintf(\"%T\", [])`                       | `list`               |\n| `u`       | Unsigned decimal integer (32-bit wrap)                       | `sprintf(\"%u\", -5)`                       | `4294967291`         |\n| `x`       | Integer in lowercase hexadecimal                             | `sprintf(\"%x\", 255)`                      | `ff`                 |\n| `X`       | Integer in uppercase hexadecimal                             | `sprintf(\"%X\", 255)`                      | `FF`                 |\n| `j`       | Python object in JSON format                                 | `sprintf(\"%j\", {\"a\": 1})`                 | `{\"a\": 1}`           |\n\n## Features\n\n### Flexible Configuration Options\n\nOur `sprintf` library offers powerful and flexible configuration options to tailor its behavior to your specific needs. You can easily adjust settings like how unmatched placeholders are handled or whether computed values are allowed. This section outlines the various ways you can configure the library.\n\n#### Chainable Configuration\n\nFor more control, you can leverage our chainable configuration interface. This allows you to set multiple configuration options in a fluent and readable manner.\n\n**Method 1: Chaining Method Calls**\n\nYou can chain configuration methods directly before calling `sprintf()`:\n\n```python\nfrom pyprintf import config\n\nresult = config() \\\n    .allow_computed_value(True) \\\n    .preserve_unmatched_placeholder(True) \\\n    .sprintf(\"My name is %s and I have %d %s. Today is %s\", \"John\", 5, lambda: \"apple\")\nprint(result)  # Output: My name is John and I have 5 apple. Today is %s\n```\n\n**Method 2: Using a Configuration Object**\n\nAlternatively, you can pass a JavaScript object containing your desired configuration options to the `config()` method:\n\n```python\nsprintf_config = config(\n    allow_computed_value=True,\n    preserve_unmatched_placeholder=True\n).sprintf(\"My name is %s and I have %d %s. Today is %s\", \"John\", 5, lambda: \"apple\")\nprint(sprintf_config)  # Output: My name is John and I have 5 apple. Today is %s\n```\n\n#### Reusing Configurations\n\nOne of the key benefits of our configuration system is the ability to create reusable configuration objects. This is particularly useful when you have consistent formatting requirements across different parts of your application.\n\n```python\nsprintf_config = config().allow_computed_value(True)\n\nresult1 = sprintf_config.sprintf(\"%s\", lambda: \"test1\")\nprint(result1)  # Output: test1\n\nresult2 = sprintf_config.sprintf(\"%s\", lambda: \"test2\") \nprint(result2)  # Output: test2\n```\n\nIn this example, `sprintfConfig` retains the `allowComputedValue(true)` setting, allowing you to apply it to multiple `sprintf()` calls without repeating the configuration.\n\n#### Analyzing placeholder statistic with getStats()\n\nA new `getStats()` method, accessible through the chainable configuration, allows you to analyze the placeholders in your format strings.\n\n```python\ncfg = config()\ncfg.sprintf(\"%s %s %s %s %(name)s %1$s %2$s\")\nprint(cfg.get_stats())\n# Output: {\n#   \"total_placeholders\": 7,\n#   \"total_named_placeholder\": 1,\n#   \"total_positional_placeholder\": 2,\n#   \"total_sequential_positional_placeholder\": 4\n# }\n```\n\n### Flexible Argument Order\n\nYou can specify the order of values in the formatted string independently from how they are provided. By adding a number (like `%1$s`, `%2$s`) to the placeholder, you control which value is used and in which position. This also allows reusing the same value multiple times without passing it again. This feature enhances the flexibility and readability of your code.\n\n__Example:__\n\n```python\nsprintf(\"%2$s is %1$s years old and loves %3$s\", 25, \"John\", \"basketball\")\n// Returns: \"John is 25 years old and loves basketball\"\n```\n\nHere, `%2$s` refers to the second argument (`John`), `%3$s` to the third (`basketball`), and `%1$s` to the first (`25`).\n\n### Named Placeholders\n\nInstead of using numbers, you can reference values by their names using objects. Placeholders are wrapped in parentheses, like `%(keyword)s`, where `keyword` matches a key in the provided object. This makes the code more readable and works with nested data, improving the maintainability of your string formatting logic.\n\n* Basic usage:\n\n__Example:__\n\n```python\nuser_obj = {\"name\": \"John\"}\nprint(sprintf(\"Hello %(name)s\", user_obj))  # Output: Hello John\n```\n\n* Nested data (dictionaries/lists):\n\n__Example:__\n\n```python\ndata = {\n    \"users\": [\n        {\"name\": \"Jane\"},\n        {\"name\": \"Jack\"}\n    ]\n}\nprint(sprintf(\"Hello %s, %(users[0].name)s, and %(users[1].name)s\", \"John\", data))\n# Output: Hello John, Jane, and Jack\n```\n\n### Named and positional placeholder\n\n`sprintf` offers exceptional flexibility by allowing you to utilize **named placeholders** (like `%(keyword)s`), **numbered positional placeholders** (such as `%1$s`, `%2$s`), and **sequential positional placeholders** (represented by `%s`). This comprehensive support enables you to choose the most appropriate style, or even combine them for complex formatting scenarios, enhancing both readability and maintainability.\n\n* Basic usage:\n\n__Example:__\n\n```python\ndata = {\"name\": \"Polly\"}\nprint(sprintf(\"%(name)s %2$s a %1$s\", \"cracker\", \"wants\", data))\n# Output: Polly wants a cracker\n```\n\n### Leveraging `preserveUnmatchedPlaceholder` functionality\n\nYou can use the `preserveUnmatchedPlaceholder` option to perform multi-stage string formatting with `sprintf`. This allows you to initially apply a subset of data, leaving unmatched placeholders in place to be filled in later.\n\n```python\ncfg = config(preserve_unmatched_placeholder=True)\nfirst_pass = cfg.sprintf(\"My name is %(firstname)s %(lastname)s\", {\"lastname\": \"Doe\"})\nprint(first_pass)  # Output: My name is %(firstname)s Doe\n\nprint(cfg.sprintf(first_pass, {\"firstname\": \"John\"}))  # Output: My name is John Doe\n```\n\n### Computed values\n\nTo generate values dynamically, you can supply a function. This function will be invoked without arguments, and its return value will be treated as the computed value.\n\nWe have exposed the `allowComputedValue` property, which allows you to enable or disable this functionality. If you intend to use `sprintf` with function arguments for dynamic values, you must explicitly enable this feature by setting `sprintf.allowComputedValue = true`. This functionality is disabled by default due to potential security concerns.\n\n**Security Consideration:**\n\nEnabling computed values introduces a risk if the format string or the arguments passed to `sprintf` come from an untrusted source. For example, a malicious actor could potentially inject a format string with a placeholder that triggers the execution of a function they also control.\n\n**Example of Potential Risk:**\n\nWhile this is a simplified illustration, imagine a scenario where user input could influence the arguments passed to `sprintf`:\n\n```python\nfrom pyprintf import config\n\n# WARNING: Enabling computed values with untrusted input is risky!\ncfg = config().allow_computed_value(True)\n\nuser_input = \"%s\"  # Could be controlled by a malicious user\n\nmalicious_function = lambda: (\n    # In a real attack, this could execute harmful code\n    print(\"Malicious function executed!\") or \n    \"dangerous output\"\n)\n\nformatted = cfg.sprintf(user_input, malicious_function)\nprint(formatted)  # Output: \"dangerous_output\"\n# Console shows: \"Malicious function executed!\"\n```\n\nIn this example, if `userInput` was crafted to include `%s` and a malicious function was somehow passed as an argument, enabling `allowComputedValue` would lead to the execution of that function.\n\n**Example (Safe Usage):**\n\nWhen using computed values with trusted input:\n\n```python\nfrom datetime import datetime\n\ncfg = config().allow_computed_value(True)\n\nresult = cfg.sprintf(\n    \"Current date and time: %s\",\n    lambda: datetime.now().strftime(\"%Y-%m-%d %H:%M:%S\")\n)\nprint(result)  # Output: \"Current date and time: 2025-04-10 13:25:07\"\n```\n\nRemember to enable `config().allow_computed_value(True)` only when you are certain about the safety and origin of the format string and its arguments.\n\n## License\n\n**pyprintf** is licensed under the terms of the BSD 3-Clause License.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fplayfulsparkle%2Fpyprintf","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fplayfulsparkle%2Fpyprintf","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fplayfulsparkle%2Fpyprintf/lists"}