roberty-code 0.1.0__py3-none-any.whl

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,52 @@
1
+ """
2
+ SDK oficial para Code Projects da Roberty.
3
+
4
+ import roberty_code as roberty
5
+
6
+ cpf = roberty.input("cpf")
7
+ roberty.output({"status": "ok"})
8
+ """
9
+
10
+ from typing import TYPE_CHECKING, Any, Optional
11
+
12
+ from . import context as _context
13
+ from ._env import ENV_VARS
14
+ from .errors import RobertyArgsError, RobertyOutputError
15
+ from .exception import ExceptionDetails, exception
16
+ from .inputs import input, inputs # noqa: A004
17
+ from .output import output
18
+ from .run import run
19
+
20
+ __all__ = [
21
+ "ENV_VARS",
22
+ "ExceptionDetails",
23
+ "RobertyArgsError",
24
+ "RobertyOutputError",
25
+ "environment",
26
+ "exception",
27
+ "input",
28
+ "inputs",
29
+ "is_roberty",
30
+ "output",
31
+ "run",
32
+ "trigger",
33
+ ]
34
+
35
+ if TYPE_CHECKING:
36
+ trigger: Optional[str]
37
+ environment: Optional[str]
38
+ is_roberty: bool
39
+
40
+ _DYNAMIC = {
41
+ "trigger": _context.trigger,
42
+ "environment": _context.environment,
43
+ "is_roberty": _context.is_roberty,
44
+ }
45
+
46
+
47
+ def __getattr__(name: str) -> Any:
48
+ """`trigger`, `environment` e `is_roberty` são lidos da env a cada acesso."""
49
+ getter = _DYNAMIC.get(name)
50
+ if getter is None:
51
+ raise AttributeError(f"module 'roberty_code' has no attribute '{name}'")
52
+ return getter()
@@ -0,0 +1,24 @@
1
+ import json
2
+ import os
3
+ from typing import Any
4
+
5
+ from ._env import ENV_VARS
6
+ from .errors import RobertyArgsError
7
+
8
+ _cache: dict[str, Any] = {}
9
+
10
+
11
+ def read_args_file() -> Any:
12
+ """Lê (com cache) o JSON apontado por `roberty-args-file-path`; `None` sem a variável."""
13
+ file_path = os.environ.get(ENV_VARS["args_file_path"])
14
+ if not file_path:
15
+ return None
16
+ if file_path in _cache:
17
+ return _cache[file_path]
18
+ try:
19
+ with open(file_path, encoding="utf-8") as file:
20
+ parsed = json.load(file)
21
+ except (OSError, ValueError) as cause:
22
+ raise RobertyArgsError(file_path, cause) from cause
23
+ _cache[file_path] = parsed
24
+ return parsed
roberty_code/_env.py ADDED
@@ -0,0 +1,8 @@
1
+ """Nomes das variáveis de ambiente injetadas pela Roberty."""
2
+
3
+ ENV_VARS = {
4
+ "environment": "roberty-environment",
5
+ "trigger": "roberty-trigger",
6
+ "args_file_path": "roberty-args-file-path",
7
+ "output_file_path": "roberty-output-file-path",
8
+ }
@@ -0,0 +1,19 @@
1
+ import os
2
+ from typing import Optional
3
+
4
+ from ._env import ENV_VARS
5
+
6
+
7
+ def trigger() -> Optional[str]:
8
+ """`manual`, `schedule`, `webhook` ou `evaluation`. `None` fora da Roberty."""
9
+ return os.environ.get(ENV_VARS["trigger"])
10
+
11
+
12
+ def environment() -> Optional[str]:
13
+ """`production` ou `preview`. `None` fora da Roberty."""
14
+ return os.environ.get(ENV_VARS["environment"])
15
+
16
+
17
+ def is_roberty() -> bool:
18
+ """`True` quando o processo roda na Roberty."""
19
+ return bool(os.environ.get(ENV_VARS["trigger"]))
roberty_code/errors.py ADDED
@@ -0,0 +1,15 @@
1
+ class RobertyArgsError(Exception):
2
+ """O arquivo de argumentos existe mas não pôde ser lido como JSON."""
3
+
4
+ def __init__(self, file_path: str, cause: BaseException) -> None:
5
+ super().__init__(
6
+ f'Roberty: não foi possível ler os argumentos em "{file_path}": {cause}'
7
+ )
8
+ self.file_path = file_path
9
+
10
+
11
+ class RobertyOutputError(Exception):
12
+ """O resultado não pôde ser serializado como JSON."""
13
+
14
+ def __init__(self, cause: BaseException) -> None:
15
+ super().__init__(f"Roberty: o resultado não pôde ser serializado como JSON: {cause}")
@@ -0,0 +1,22 @@
1
+ from typing import Optional, TypedDict
2
+
3
+ from ._args_file import read_args_file
4
+
5
+
6
+ class ExceptionDetails(TypedDict):
7
+ message: str
8
+ stack: Optional[str]
9
+ exitCode: int
10
+
11
+
12
+ def exception() -> Optional[ExceptionDetails]:
13
+ """Detalhes do erro dentro do `exceptionHandler`. `None` fora dele."""
14
+ details = read_args_file()
15
+ if not isinstance(details, dict):
16
+ return None
17
+ stack = details.get("stack")
18
+ return ExceptionDetails(
19
+ message=str(details.get("message", "")),
20
+ stack=stack if isinstance(stack, str) else None,
21
+ exitCode=int(details.get("exitCode", 1)),
22
+ )
roberty_code/inputs.py ADDED
@@ -0,0 +1,22 @@
1
+ from typing import Any, Optional, TypeVar, Union, overload
2
+
3
+ from ._args_file import read_args_file
4
+
5
+ T = TypeVar("T")
6
+
7
+
8
+ def inputs() -> Optional[Any]:
9
+ """Todos os argumentos da execução (`webhook`/`evaluation`). `None` em `manual`/`schedule`."""
10
+ return read_args_file()
11
+
12
+
13
+ @overload
14
+ def input(name: str) -> Optional[Any]: ... # noqa: A001
15
+ @overload
16
+ def input(name: str, default: T) -> Union[Any, T]: ... # noqa: A001
17
+ def input(name: str, default: Any = None) -> Any: # noqa: A001
18
+ """Um argumento específico. `default` quando não existe ou não há argumentos."""
19
+ args = read_args_file()
20
+ if not isinstance(args, dict):
21
+ return default
22
+ return args.get(name, default)
roberty_code/output.py ADDED
@@ -0,0 +1,47 @@
1
+ import json
2
+ import os
3
+ import sys
4
+ from typing import Any
5
+
6
+ from ._env import ENV_VARS
7
+ from .errors import RobertyOutputError
8
+
9
+ OUTPUT_LIMIT_BYTES = 256 * 1024
10
+
11
+ _state = {"warned_missing_path": False}
12
+
13
+
14
+ def output(value: Any) -> bool:
15
+ """Grava `value` como JSON no arquivo de resultado; `False` fora da Roberty (aviso único)."""
16
+ serialized = _serialize(value)
17
+ file_path = os.environ.get(ENV_VARS["output_file_path"])
18
+ if not file_path:
19
+ _warn_missing_path_once()
20
+ return False
21
+ if len(serialized.encode("utf-8")) > OUTPUT_LIMIT_BYTES:
22
+ print(
23
+ "Roberty: resultado acima de 256 KB; "
24
+ "em execuções webhook a plataforma corta o excedente.",
25
+ file=sys.stderr,
26
+ )
27
+ with open(file_path, "w", encoding="utf-8") as file:
28
+ file.write(serialized)
29
+ return True
30
+
31
+
32
+ def _serialize(value: Any) -> str:
33
+ try:
34
+ return json.dumps(value, ensure_ascii=False)
35
+ except (TypeError, ValueError) as cause:
36
+ raise RobertyOutputError(cause) from cause
37
+
38
+
39
+ def _warn_missing_path_once() -> None:
40
+ if _state["warned_missing_path"]:
41
+ return
42
+ _state["warned_missing_path"] = True
43
+ print(
44
+ f'Roberty: "{ENV_VARS["output_file_path"]}" não definida; '
45
+ "o resultado não foi gravado (execução fora da Roberty).",
46
+ file=sys.stderr,
47
+ )
roberty_code/py.typed ADDED
File without changes
roberty_code/run.py ADDED
@@ -0,0 +1,36 @@
1
+ import asyncio
2
+ import inspect
3
+ import json
4
+ import sys
5
+ import traceback
6
+ from typing import Any, Callable
7
+
8
+ from .context import is_roberty
9
+ from .output import output
10
+
11
+
12
+ def run(handler: Callable[[Any], Any], exit_on_complete: bool = True) -> Any:
13
+ """
14
+ Atalho opcional: executa `handler(roberty)`, grava o retorno como resultado e encerra
15
+ com exit code 0/1. Fora da Roberty imprime o resultado no stdout. Aceita `async def`.
16
+ """
17
+ import roberty_code
18
+
19
+ try:
20
+ result = handler(roberty_code)
21
+ if inspect.isawaitable(result):
22
+ result = asyncio.run(result)
23
+ if result is not None:
24
+ output(result)
25
+ if not is_roberty():
26
+ print(json.dumps(result, indent=2, ensure_ascii=False))
27
+ if exit_on_complete:
28
+ sys.exit(0)
29
+ return result
30
+ except SystemExit:
31
+ raise
32
+ except BaseException:
33
+ traceback.print_exc()
34
+ if exit_on_complete:
35
+ sys.exit(1)
36
+ raise
@@ -0,0 +1,96 @@
1
+ Metadata-Version: 2.4
2
+ Name: roberty-code
3
+ Version: 0.1.0
4
+ Summary: SDK oficial para Code Projects da Roberty: leia argumentos, escreva o resultado e descubra o gatilho da execução.
5
+ Project-URL: Homepage, https://github.com/robertyautomation/code-sdk/tree/main/packages/python#readme
6
+ Project-URL: Repository, https://github.com/robertyautomation/code-sdk
7
+ Project-URL: Issues, https://github.com/robertyautomation/code-sdk/issues
8
+ Project-URL: Documentation, https://github.com/robertyautomation/code/blob/main/roberty-code-project-guide.md
9
+ Author: Roberty Automation
10
+ License-Expression: MIT
11
+ License-File: LICENSE
12
+ Keywords: automation,code-project,roberty,rpa,sdk
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: Operating System :: OS Independent
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3 :: Only
18
+ Classifier: Programming Language :: Python :: 3.9
19
+ Classifier: Programming Language :: Python :: 3.10
20
+ Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Classifier: Programming Language :: Python :: 3.13
23
+ Classifier: Topic :: Software Development :: Libraries
24
+ Classifier: Typing :: Typed
25
+ Requires-Python: >=3.9
26
+ Description-Content-Type: text/markdown
27
+
28
+ # roberty-code
29
+
30
+ SDK oficial para **Code Projects** da [Roberty](https://roberty.app) em Python. Leia argumentos, escreva o resultado e descubra o gatilho da execução sem lidar com variáveis de ambiente.
31
+
32
+ ```bash
33
+ pip install roberty-code
34
+ ```
35
+
36
+ Python ≥ 3.9. Sem dependências. Tipado.
37
+
38
+ ## Uso
39
+
40
+ ```python
41
+ from datetime import datetime, timezone
42
+ import roberty_code as roberty
43
+
44
+ print(f"Iniciando ({roberty.environment}, gatilho: {roberty.trigger})")
45
+
46
+ cpf = roberty.input("cpf") # um argumento
47
+ args = roberty.inputs() # todos os argumentos (ou None)
48
+
49
+ # ... lógica do robô ...
50
+
51
+ roberty.output({"status": "ok", "processedAt": datetime.now(timezone.utc).isoformat()})
52
+ ```
53
+
54
+ Fora da Roberty o mesmo código roda: `inputs()` devolve `None`, `output()` avisa uma vez e devolve `False`.
55
+
56
+ ## API
57
+
58
+ | Membro | Retorno | Descrição |
59
+ | --- | --- | --- |
60
+ | `roberty.trigger` | `str \| None` | `manual`, `schedule`, `webhook` ou `evaluation` |
61
+ | `roberty.environment` | `str \| None` | `production` ou `preview` |
62
+ | `roberty.is_roberty` | `bool` | `True` quando roda na Roberty |
63
+ | `roberty.inputs()` | `Any \| None` | Todos os argumentos (`webhook`/`evaluation`) |
64
+ | `roberty.input(name, default=None)` | `Any` | Um argumento específico |
65
+ | `roberty.output(value)` | `bool` | Grava o resultado (JSON) lido pela plataforma |
66
+ | `roberty.exception()` | `ExceptionDetails \| None` | `{"message", "stack", "exitCode"}` dentro do `exceptionHandler` |
67
+ | `roberty.run(handler, exit_on_complete=True)` | `Any` | **Opcional**: executa `handler(roberty)`, grava o retorno e encerra com exit code `0`/`1`. Aceita `async def` |
68
+ | `roberty.ENV_VARS` | `dict` | Nomes das variáveis `roberty-*` |
69
+
70
+ Erros: `RobertyArgsError` (arquivo de argumentos com JSON inválido) e `RobertyOutputError` (valor não serializável; converta `datetime` com `.isoformat()`).
71
+
72
+ ### `exceptionHandler`
73
+
74
+ ```python
75
+ # on_error.py — declarado em roberty.json como "exceptionHandler": "on_error.py"
76
+ import roberty_code as roberty
77
+
78
+ error = roberty.exception()
79
+ print(f"Robô falhou (exit code {error['exitCode']}): {error['message']}")
80
+ ```
81
+
82
+ ### Atalho `run`
83
+
84
+ ```python
85
+ roberty.run(lambda r: {"status": "ok", "cpf": r.input("cpf")})
86
+ ```
87
+
88
+ Encerra o processo ao terminar; passe `exit_on_complete=False` para embutir em testes.
89
+
90
+ ## Documentação
91
+
92
+ [Guia de Code Projects](https://github.com/robertyautomation/code/blob/main/roberty-code-project-guide.md): contrato de execução, `roberty.json`, gatilhos e webhook.
93
+
94
+ ## Licença
95
+
96
+ MIT. Copyright © Roberty Automation. Todos os direitos reservados.
@@ -0,0 +1,14 @@
1
+ roberty_code/__init__.py,sha256=UR05kr3qyLnU4pU1EkSM4QrF8gioQAmVYfIJ_Wj9tXQ,1200
2
+ roberty_code/_args_file.py,sha256=e7Eh3IlYyJyFLccVS_4AsflJJcCsxXz9PycTXsMLESo,682
3
+ roberty_code/_env.py,sha256=-GmFtPw3G4A_DrSV0FbkslJM1QGC8iACnBb2XJEN1n8,255
4
+ roberty_code/context.py,sha256=xDyIcCYp9YWn09Gf1Fmhz7qIBnpDLUZX6asXm-HNF1o,513
5
+ roberty_code/errors.py,sha256=rM8uu29fu16WuaqrWi0WLA3JWJLnD2bVYLDeFh2IYl4,590
6
+ roberty_code/exception.py,sha256=LZHF-CYSq4B28STRfphWoCX5r8Dj2xxZT5p1hClg7yM,613
7
+ roberty_code/inputs.py,sha256=5IUWLHAx7v5kiTdOujqOysnNq2CQnAUpHBAPzMsseFc,701
8
+ roberty_code/output.py,sha256=c4Hd6c-fjcHSfwnN-zqLE5rSLivn0mqGabaOcWlCxQ8,1354
9
+ roberty_code/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
+ roberty_code/run.py,sha256=U4TQdXdahwq7RXlBcFb6muSunKWrxMon8T02JSu_vJQ,991
11
+ roberty_code-0.1.0.dist-info/METADATA,sha256=LefAiPYdwoadDjuvpK1HJVDP11SJz3b74UR1581jA44,3852
12
+ roberty_code-0.1.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
13
+ roberty_code-0.1.0.dist-info/licenses/LICENSE,sha256=ut1mZ0GFLRXhMBelw08WVhY3Ww6bZLVV5QmJZRNZdNA,1075
14
+ roberty_code-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.27.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Roberty Automation
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.