From 4ffbcdf1d3c8c2fbaf7152d207b24cdb0ea82ac9 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Tue, 19 Dec 2023 13:21:58 -0500 Subject: [PATCH 1/8] chore(internal): fix typos (#287) --- src/anthropic/_base_client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/anthropic/_base_client.py b/src/anthropic/_base_client.py index 92189617..481171a4 100644 --- a/src/anthropic/_base_client.py +++ b/src/anthropic/_base_client.py @@ -107,7 +107,7 @@ class PageInfo: - """Stores the necesary information to build the request to retrieve the next page. + """Stores the necessary information to build the request to retrieve the next page. Either `url` or `params` must be set. """ From eec4574f1f6668804c88bda67b901db10400fbc3 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Tue, 19 Dec 2023 13:53:28 -0500 Subject: [PATCH 2/8] docs(readme): remove old migration guide (#289) --- README.md | 64 ------------------------------------------------------- 1 file changed, 64 deletions(-) diff --git a/README.md b/README.md index 82e8c20c..ae5c548f 100644 --- a/README.md +++ b/README.md @@ -8,70 +8,6 @@ and offers both synchronous and asynchronous clients powered by [httpx](https:// For the AWS Bedrock API, see [`anthropic-bedrock`](https://github.com/anthropics/anthropic-bedrock-python). -## Migration from v0.2.x and below - -In `v0.3.0`, we introduced a fully rewritten SDK. - -The new version uses separate sync and async clients, unified streaming, typed params and structured response objects, and resource-oriented methods: - -**Sync before/after:** - -```diff -- client = anthropic.Client(os.environ["ANTHROPIC_API_KEY"]) -+ client = anthropic.Anthropic(api_key=os.environ["ANTHROPIC_API_KEY"]) - # or, simply provide an ANTHROPIC_API_KEY environment variable: -+ client = anthropic.Anthropic() - -- rsp = client.completion(**params) -- rsp["completion"] -+ rsp = client.completions.create(**params) -+ rsp.completion -``` - -**Async before/after:** - -```diff -- client = anthropic.Client(os.environ["ANTHROPIC_API_KEY"]) -+ client = anthropic.AsyncAnthropic(api_key=os.environ["ANTHROPIC_API_KEY"]) - -- await client.acompletion(**params) -+ await client.completions.create(**params) -``` - -The `.completion_stream()` and `.acompletion_stream()` methods have been removed; -simply pass `stream=True`to `.completions.create()`. - -Streaming responses are now incremental; the full text is not sent in each message, -as v0.3 sends the `Anthropic-Version: 2023-06-01` header. - -
-Example streaming diff - -```diff py - import anthropic - -- client = anthropic.Client(os.environ["ANTHROPIC_API_KEY"]) -+ client = anthropic.Anthropic() - - # Streams are now incremental diffs of text - # rather than sending the whole message every time: - text = " -- stream = client.completion_stream(**params) -- for data in stream: -- diff = data["completion"].replace(text, "") -- text = data["completion"] -+ stream = client.completions.create(**params, stream=True) -+ for data in stream: -+ diff = data.completion # incremental text -+ text += data.completion - print(diff, end="") - - print("Done. Final text is:") - print(text) -``` - -
- ## Documentation The API documentation can be found [here](https://docs.anthropic.com/claude/reference/). From 9ec5c57ba9a14a769d540e48755b05a1c190b45b Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Tue, 19 Dec 2023 14:04:35 -0500 Subject: [PATCH 3/8] chore(package): bump minimum typing-extensions to 4.7 (#290) --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index e18918f3..905f3cee 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,7 +10,7 @@ authors = [ dependencies = [ "httpx>=0.23.0, <1", "pydantic>=1.9.0, <3", - "typing-extensions>=4.5, <5", + "typing-extensions>=4.7, <5", "anyio>=3.5.0, <5", "distro>=1.7.0, <2", "sniffio", From d18a895d380fc0c6610443486d73247b0cd97376 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Wed, 20 Dec 2023 20:21:52 -0500 Subject: [PATCH 4/8] docs(messages): improvements to helpers reference + typos (#291) --- README.md | 7 ++--- examples/messages_stream_handler.py | 3 +-- helpers.md | 40 ++++++++++++++++++++++++++++- 3 files changed, 42 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index ae5c548f..a61754bd 100644 --- a/README.md +++ b/README.md @@ -131,11 +131,8 @@ async def main() -> None: print(text, end="", flush=True) print() - # you can still get the accumulated final message outside of - # the context manager, as long as the entire stream was consumed - # inside of the context manager - accumulated = await stream.get_final_message() - print(accumulated.model_dump_json(indent=2)) + message = await stream.get_final_message() + print(message.model_dump_json(indent=2)) asyncio.run(main()) ``` diff --git a/examples/messages_stream_handler.py b/examples/messages_stream_handler.py index 41f982f7..a0175d6f 100644 --- a/examples/messages_stream_handler.py +++ b/examples/messages_stream_handler.py @@ -1,9 +1,8 @@ import asyncio from typing_extensions import override -from anthropic import AsyncAnthropic +from anthropic import AsyncAnthropic, AsyncMessageStream from anthropic.types.beta import MessageStreamEvent -from anthropic.lib.streaming import AsyncMessageStream client = AsyncAnthropic() diff --git a/helpers.md b/helpers.md index 821cae62..a5edcaf8 100644 --- a/helpers.md +++ b/helpers.md @@ -21,7 +21,7 @@ async with client.beta.messages.stream( `client.beta.messages.stream()` returns a `MessageStreamManager`, which is a context manager that yields a `MessageStream` which is iterable, emits events and accumulates messages. Alternatively, you can use `client.beta.messages.create(..., stream=True)` which returns an -iteratable of the events in the stream and uses less memory (most notably, it does not accumulate a final message +iterable of the events in the stream and uses less memory (most notably, it does not accumulate a final message object for you). The stream will be cancelled when the context manager exits but you can also close it prematurely by calling `stream.close()`. @@ -45,6 +45,44 @@ print() ### Events +You can pass an `event_handler` argument to `client.beta.messages.stream` to register callback methods that are fired when certain events happen: + +```py +import asyncio +from typing_extensions import override + +from anthropic import AsyncAnthropic, AsyncMessageStream +from anthropic.types.beta import MessageStreamEvent + +client = AsyncAnthropic() + +class MyStream(AsyncMessageStream): + @override + async def on_text(self, delta: str) -> None: + print(text, end="", flush=True) + + @override + async def on_stream_event(self, event: MessageStreamEvent) -> None: + print("on_event fired with:", event) + +async def main() -> None: + async with client.beta.messages.stream( + max_tokens=1024, + messages=[ + { + "role": "user", + "content": "Say hello there!", + } + ], + model="claude-2.1", + event_handler=MyStream, + ) as stream: + message = await stream.get_final_message() + print("accumulated message: ", message.model_dump_json(indent=2)) + +asyncio.run(main()) +``` + #### `await on_stream_event(event: MessageStreamEvent)` The event is fired when an event is received from the API. From ba2953dcaa8a8fcebaa7e8891304687c95b17499 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Thu, 21 Dec 2023 07:39:09 -0500 Subject: [PATCH 5/8] chore(internal): add bin script (#292) --- bin/check-env-state.py | 40 ++++++++++++++++++++++++++++++++++++++++ pyproject.toml | 1 + requirements-dev.lock | 4 +++- 3 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 bin/check-env-state.py diff --git a/bin/check-env-state.py b/bin/check-env-state.py new file mode 100644 index 00000000..e1b8b6cb --- /dev/null +++ b/bin/check-env-state.py @@ -0,0 +1,40 @@ +"""Script that exits 1 if the current environment is not +in sync with the `requirements-dev.lock` file. +""" + +from pathlib import Path + +import importlib_metadata + + +def should_run_sync() -> bool: + dev_lock = Path(__file__).parent.parent.joinpath("requirements-dev.lock") + + for line in dev_lock.read_text().splitlines(): + if not line or line.startswith("#") or line.startswith("-e"): + continue + + dep, lock_version = line.split("==") + + try: + version = importlib_metadata.version(dep) + + if lock_version != version: + print(f"mismatch for {dep} current={version} lock={lock_version}") + return True + except Exception: + print(f"could not import {dep}") + return True + + return False + + +def main() -> None: + if should_run_sync(): + exit(1) + else: + exit(0) + + +if __name__ == "__main__": + main() diff --git a/pyproject.toml b/pyproject.toml index 905f3cee..2e837c62 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -58,6 +58,7 @@ dev-dependencies = [ "time-machine", "nox", "dirty-equals>=0.6.0", + "importlib-metadata>=6.7.0", ] diff --git a/requirements-dev.lock b/requirements-dev.lock index f1d546fc..07c2e0e5 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -21,12 +21,13 @@ distlib==0.3.7 distro==1.8.0 exceptiongroup==1.1.3 filelock==3.12.4 -fsspec==2023.12.1 +fsspec==2023.12.2 h11==0.14.0 httpcore==1.0.2 httpx==0.25.2 huggingface-hub==0.16.4 idna==3.4 +importlib-metadata==7.0.0 iniconfig==2.0.0 isort==5.10.1 mypy==1.7.1 @@ -58,5 +59,6 @@ tqdm==4.66.1 typing-extensions==4.8.0 urllib3==2.1.0 virtualenv==20.24.5 +zipp==3.17.0 # The following packages are considered to be unsafe in a requirements file: setuptools==68.2.2 From ae6c59fab76e6736b469a44e6d6b1f884a9d77b3 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Thu, 21 Dec 2023 22:47:39 -0500 Subject: [PATCH 6/8] test: run the garbage collector less often (#293) --- tests/test_client.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_client.py b/tests/test_client.py index f474752c..2fe4afe6 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -208,8 +208,8 @@ def build_request(options: FinalRequestOptions) -> None: ITERATIONS = 10 for _ in range(ITERATIONS): build_request(options) - gc.collect() + gc.collect() snapshot_after = tracemalloc.take_snapshot() tracemalloc.stop() @@ -964,8 +964,8 @@ def build_request(options: FinalRequestOptions) -> None: ITERATIONS = 10 for _ in range(ITERATIONS): build_request(options) - gc.collect() + gc.collect() snapshot_after = tracemalloc.take_snapshot() tracemalloc.stop() From 1753887a776f41bdc2d648329cfe6f20c91125e5 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Fri, 22 Dec 2023 05:44:58 -0500 Subject: [PATCH 7/8] chore(internal): use ruff instead of black for formatting (#294) --- bin/{blacken-docs.py => ruffen-docs.py} | 130 ++++------------------- pyproject.toml | 12 ++- requirements-dev.lock | 5 +- src/anthropic/_models.py | 2 +- src/anthropic/_types.py | 16 +-- src/anthropic/_utils/_transform.py | 5 +- src/anthropic/_utils/_utils.py | 4 +- src/anthropic/lib/streaming/_messages.py | 12 +-- tests/test_transform.py | 4 +- 9 files changed, 56 insertions(+), 134 deletions(-) rename bin/{blacken-docs.py => ruffen-docs.py} (52%) diff --git a/bin/blacken-docs.py b/bin/ruffen-docs.py similarity index 52% rename from bin/blacken-docs.py rename to bin/ruffen-docs.py index 45d0ad12..37b3d94f 100644 --- a/bin/blacken-docs.py +++ b/bin/ruffen-docs.py @@ -1,16 +1,14 @@ -# fork of https://github.com/asottile/blacken-docs implementing https://github.com/asottile/blacken-docs/issues/170 +# fork of https://github.com/asottile/blacken-docs adapted for ruff from __future__ import annotations import re +import sys import argparse import textwrap import contextlib +import subprocess from typing import Match, Optional, Sequence, Generator, NamedTuple, cast -import black -from black.mode import TargetVersion -from black.const import DEFAULT_LINE_LENGTH - MD_RE = re.compile( r"(?P^(?P *)```\s*python\n)" r"(?P.*?)" r"(?P^(?P=indent)```\s*$)", re.DOTALL | re.MULTILINE, @@ -19,55 +17,12 @@ r"(?P^(?P *)```\s*pycon\n)" r"(?P.*?)" r"(?P^(?P=indent)```.*$)", re.DOTALL | re.MULTILINE, ) -RST_PY_LANGS = frozenset(("python", "py", "sage", "python3", "py3", "numpy")) -BLOCK_TYPES = "(code|code-block|sourcecode|ipython)" -DOCTEST_TYPES = "(testsetup|testcleanup|testcode)" -RST_RE = re.compile( - rf"(?P" - rf"^(?P *)\.\. (" - rf"jupyter-execute::|" - rf"{BLOCK_TYPES}:: (?P\w+)|" - rf"{DOCTEST_TYPES}::.*" - rf")\n" - rf"((?P=indent) +:.*\n)*" - rf"\n*" - rf")" - rf"(?P(^((?P=indent) +.*)?\n)+)", - re.MULTILINE, -) -RST_PYCON_RE = re.compile( - r"(?P" - r"(?P *)\.\. ((code|code-block):: pycon|doctest::.*)\n" - r"((?P=indent) +:.*\n)*" - r"\n*" - r")" - r"(?P(^((?P=indent) +.*)?(\n|$))+)", - re.MULTILINE, -) PYCON_PREFIX = ">>> " PYCON_CONTINUATION_PREFIX = "..." PYCON_CONTINUATION_RE = re.compile( rf"^{re.escape(PYCON_CONTINUATION_PREFIX)}( |$)", ) -LATEX_RE = re.compile( - r"(?P^(?P *)\\begin{minted}{python}\n)" - r"(?P.*?)" - r"(?P^(?P=indent)\\end{minted}\s*$)", - re.DOTALL | re.MULTILINE, -) -LATEX_PYCON_RE = re.compile( - r"(?P^(?P *)\\begin{minted}{pycon}\n)" r"(?P.*?)" r"(?P^(?P=indent)\\end{minted}\s*$)", - re.DOTALL | re.MULTILINE, -) -PYTHONTEX_LANG = r"(?Ppyblock|pycode|pyconsole|pyverbatim)" -PYTHONTEX_RE = re.compile( - rf"(?P^(?P *)\\begin{{{PYTHONTEX_LANG}}}\n)" - rf"(?P.*?)" - rf"(?P^(?P=indent)\\end{{(?P=lang)}}\s*$)", - re.DOTALL | re.MULTILINE, -) -INDENT_RE = re.compile("^ +(?=[^ ])", re.MULTILINE) -TRAILING_NL_RE = re.compile(r"\n+\Z", re.MULTILINE) +DEFAULT_LINE_LENGTH = 100 class CodeBlockError(NamedTuple): @@ -77,7 +32,6 @@ class CodeBlockError(NamedTuple): def format_str( src: str, - black_mode: black.FileMode, ) -> tuple[str, Sequence[CodeBlockError]]: errors: list[CodeBlockError] = [] @@ -91,24 +45,10 @@ def _collect_error(match: Match[str]) -> Generator[None, None, None]: def _md_match(match: Match[str]) -> str: code = textwrap.dedent(match["code"]) with _collect_error(match): - code = black.format_str(code, mode=black_mode) + code = format_code_block(code) code = textwrap.indent(code, match["indent"]) return f'{match["before"]}{code}{match["after"]}' - def _rst_match(match: Match[str]) -> str: - lang = match["lang"] - if lang is not None and lang not in RST_PY_LANGS: - return match[0] - min_indent = min(INDENT_RE.findall(match["code"])) - trailing_ws_match = TRAILING_NL_RE.search(match["code"]) - assert trailing_ws_match - trailing_ws = trailing_ws_match.group() - code = textwrap.dedent(match["code"]) - with _collect_error(match): - code = black.format_str(code, mode=black_mode) - code = textwrap.indent(code, min_indent) - return f'{match["before"]}{code.rstrip()}{trailing_ws}' - def _pycon_match(match: Match[str]) -> str: code = "" fragment = cast(Optional[str], None) @@ -119,7 +59,7 @@ def finish_fragment() -> None: if fragment is not None: with _collect_error(match): - fragment = black.format_str(fragment, mode=black_mode) + fragment = format_code_block(fragment) fragment_lines = fragment.splitlines() code += f"{PYCON_PREFIX}{fragment_lines[0]}\n" for line in fragment_lines[1:]: @@ -159,42 +99,33 @@ def _md_pycon_match(match: Match[str]) -> str: code = textwrap.indent(code, match["indent"]) return f'{match["before"]}{code}{match["after"]}' - def _rst_pycon_match(match: Match[str]) -> str: - code = _pycon_match(match) - min_indent = min(INDENT_RE.findall(match["code"])) - code = textwrap.indent(code, min_indent) - return f'{match["before"]}{code}' - - def _latex_match(match: Match[str]) -> str: - code = textwrap.dedent(match["code"]) - with _collect_error(match): - code = black.format_str(code, mode=black_mode) - code = textwrap.indent(code, match["indent"]) - return f'{match["before"]}{code}{match["after"]}' - - def _latex_pycon_match(match: Match[str]) -> str: - code = _pycon_match(match) - code = textwrap.indent(code, match["indent"]) - return f'{match["before"]}{code}{match["after"]}' - src = MD_RE.sub(_md_match, src) src = MD_PYCON_RE.sub(_md_pycon_match, src) - src = RST_RE.sub(_rst_match, src) - src = RST_PYCON_RE.sub(_rst_pycon_match, src) - src = LATEX_RE.sub(_latex_match, src) - src = LATEX_PYCON_RE.sub(_latex_pycon_match, src) - src = PYTHONTEX_RE.sub(_latex_match, src) return src, errors +def format_code_block(code: str) -> str: + return subprocess.check_output( + [ + sys.executable, + "-m", + "ruff", + "format", + "--stdin-filename=script.py", + f"--line-length={DEFAULT_LINE_LENGTH}", + ], + encoding="utf-8", + input=code, + ) + + def format_file( filename: str, - black_mode: black.FileMode, skip_errors: bool, ) -> int: with open(filename, encoding="UTF-8") as f: contents = f.read() - new_contents, errors = format_str(contents, black_mode) + new_contents, errors = format_str(contents) for error in errors: lineno = contents[: error.offset].count("\n") + 1 print(f"{filename}:{lineno}: code block parse error {error.exc}") @@ -217,15 +148,6 @@ def main(argv: Sequence[str] | None = None) -> int: type=int, default=DEFAULT_LINE_LENGTH, ) - parser.add_argument( - "-t", - "--target-version", - action="append", - type=lambda v: TargetVersion[v.upper()], - default=[], - help=f"choices: {[v.name.lower() for v in TargetVersion]}", - dest="target_versions", - ) parser.add_argument( "-S", "--skip-string-normalization", @@ -235,15 +157,9 @@ def main(argv: Sequence[str] | None = None) -> int: parser.add_argument("filenames", nargs="*") args = parser.parse_args(argv) - black_mode = black.FileMode( - target_versions=set(args.target_versions), - line_length=args.line_length, - string_normalization=not args.skip_string_normalization, - ) - retv = 0 for filename in args.filenames: - retv |= format_file(filename, black_mode, skip_errors=args.skip_errors) + retv |= format_file(filename, skip_errors=args.skip_errors) return retv diff --git a/pyproject.toml b/pyproject.toml index 2e837c62..0993fdcc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -49,7 +49,6 @@ managed = true dev-dependencies = [ "pyright", "mypy", - "black", "respx", "pytest", "pytest-asyncio", @@ -64,17 +63,18 @@ dev-dependencies = [ [tool.rye.scripts] format = { chain = [ - "format:black", - "format:docs", "format:ruff", + "format:docs", + "fix:ruff", "format:isort", ]} "format:black" = "black ." -"format:docs" = "python bin/blacken-docs.py README.md api.md" -"format:ruff" = "ruff --fix ." +"format:docs" = "python bin/ruffen-docs.py README.md api.md" +"format:ruff" = "ruff format" "format:isort" = "isort ." "check:ruff" = "ruff ." +"fix:ruff" = "ruff --fix ." typecheck = { chain = [ "typecheck:pyright", @@ -160,6 +160,8 @@ unfixable = [ ] ignore-init-module-imports = true +[tool.ruff.format] +docstring-code-format = true [tool.ruff.per-file-ignores] "bin/**.py" = ["T201", "T203"] diff --git a/requirements-dev.lock b/requirements-dev.lock index 07c2e0e5..59453708 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -11,10 +11,8 @@ annotated-types==0.6.0 anyio==4.1.0 argcomplete==3.1.2 attrs==23.1.0 -black==23.3.0 certifi==2023.7.22 charset-normalizer==3.3.2 -click==8.1.7 colorlog==6.7.0 dirty-equals==0.6.0 distlib==0.3.7 @@ -35,7 +33,6 @@ mypy-extensions==1.0.0 nodeenv==1.8.0 nox==2023.4.22 packaging==23.2 -pathspec==0.11.2 platformdirs==3.11.0 pluggy==1.3.0 py==1.11.0 @@ -49,7 +46,7 @@ pytz==2023.3.post1 pyyaml==6.0.1 requests==2.31.0 respx==0.20.2 -ruff==0.1.7 +ruff==0.1.9 six==1.16.0 sniffio==1.3.0 time-machine==2.9.0 diff --git a/src/anthropic/_models.py b/src/anthropic/_models.py index 5b8c9601..330a2064 100644 --- a/src/anthropic/_models.py +++ b/src/anthropic/_models.py @@ -382,7 +382,7 @@ class RootModel(GenericModel, Generic[_T]): For example: ```py - validated = RootModel[int](__root__='5').__root__ + validated = RootModel[int](__root__="5").__root__ # validated: 5 ``` """ diff --git a/src/anthropic/_types.py b/src/anthropic/_types.py index 4d4ebf8a..8372db4f 100644 --- a/src/anthropic/_types.py +++ b/src/anthropic/_types.py @@ -278,11 +278,13 @@ class NotGiven: For example: ```py - def get(timeout: Union[int, NotGiven, None] = NotGiven()) -> Response: ... + def get(timeout: Union[int, NotGiven, None] = NotGiven()) -> Response: + ... + - get(timeout=1) # 1s timeout - get(timeout=None) # No timeout - get() # Default timeout behavior, which may not be statically known at the method definition. + get(timeout=1) # 1s timeout + get(timeout=None) # No timeout + get() # Default timeout behavior, which may not be statically known at the method definition. ``` """ @@ -304,14 +306,14 @@ class Omit: ```py # as the default `Content-Type` header is `application/json` that will be sent - client.post('/upload/files', files={'file': b'my raw file content'}) + client.post("/upload/files", files={"file": b"my raw file content"}) # you can't explicitly override the header as it has to be dynamically generated # to look something like: 'multipart/form-data; boundary=0d8382fcf5f8c3be01ca2e11002d2983' - client.post(..., headers={'Content-Type': 'multipart/form-data'}) + client.post(..., headers={"Content-Type": "multipart/form-data"}) # instead you can remove the default `application/json` header by passing Omit - client.post(..., headers={'Content-Type': Omit()}) + client.post(..., headers={"Content-Type": Omit()}) ``` """ diff --git a/src/anthropic/_utils/_transform.py b/src/anthropic/_utils/_transform.py index 91175590..342b5241 100644 --- a/src/anthropic/_utils/_transform.py +++ b/src/anthropic/_utils/_transform.py @@ -80,9 +80,10 @@ def transform( ```py class Params(TypedDict, total=False): - card_id: Required[Annotated[str, PropertyInfo(alias='cardID')]] + card_id: Required[Annotated[str, PropertyInfo(alias="cardID")]] - transformed = transform({'card_id': ''}, Params) + + transformed = transform({"card_id": ""}, Params) # {'cardID': ''} ``` diff --git a/src/anthropic/_utils/_utils.py b/src/anthropic/_utils/_utils.py index 993462a6..cc624b0c 100644 --- a/src/anthropic/_utils/_utils.py +++ b/src/anthropic/_utils/_utils.py @@ -211,13 +211,15 @@ def required_args(*variants: Sequence[str]) -> Callable[[CallableT], CallableT]: def foo(*, a: str) -> str: ... + @overload def foo(*, b: bool) -> str: ... + # This enforces the same constraints that a static type checker would # i.e. that either a or b must be passed to the function - @required_args(['a'], ['b']) + @required_args(["a"], ["b"]) def foo(*, a: str | None = None, b: bool | None = None) -> str: ... ``` diff --git a/src/anthropic/lib/streaming/_messages.py b/src/anthropic/lib/streaming/_messages.py index 987acd16..76accf0f 100644 --- a/src/anthropic/lib/streaming/_messages.py +++ b/src/anthropic/lib/streaming/_messages.py @@ -105,9 +105,9 @@ def on_text(self, text: str, snapshot: str) -> None: text, for example: ```py - on_text('Hello', 'Hello') - on_text(' there', 'Hello there') - on_text('!', 'Hello there!') + on_text("Hello", "Hello") + on_text(" there", "Hello there") + on_text("!", "Hello there!") ``` """ @@ -295,9 +295,9 @@ async def on_text(self, text: str, snapshot: str) -> None: text, for example: ``` - on_text('Hello', 'Hello') - on_text(' there', 'Hello there') - on_text('!', 'Hello there!') + on_text("Hello", "Hello") + on_text(" there", "Hello there") + on_text("!", "Hello there!") ``` """ diff --git a/tests/test_transform.py b/tests/test_transform.py index cfcf870e..1d1fb9bc 100644 --- a/tests/test_transform.py +++ b/tests/test_transform.py @@ -189,7 +189,9 @@ class DateDictWithRequiredAlias(TypedDict, total=False): def test_datetime_with_alias() -> None: assert transform({"required_prop": None}, DateDictWithRequiredAlias) == {"prop": None} # type: ignore[comparison-overlap] - assert transform({"required_prop": date.fromisoformat("2023-02-23")}, DateDictWithRequiredAlias) == {"prop": "2023-02-23"} # type: ignore[comparison-overlap] + assert transform({"required_prop": date.fromisoformat("2023-02-23")}, DateDictWithRequiredAlias) == { + "prop": "2023-02-23" + } # type: ignore[comparison-overlap] class MyModel(BaseModel): From 77270e3b634d931436bbdac58181858bf2055cb9 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Fri, 22 Dec 2023 05:45:20 -0500 Subject: [PATCH 8/8] release: 0.8.1 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 17 +++++++++++++++++ pyproject.toml | 2 +- src/anthropic/_version.py | 2 +- 4 files changed, 20 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 6538ca91..2b28d6ec 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.8.0" + ".": "0.8.1" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 7feae44c..0b36bdd8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,22 @@ # Changelog +## 0.8.1 (2023-12-22) + +Full Changelog: [v0.8.0...v0.8.1](https://github.com/anthropics/anthropic-sdk-python/compare/v0.8.0...v0.8.1) + +### Chores + +* **internal:** add bin script ([#292](https://github.com/anthropics/anthropic-sdk-python/issues/292)) ([ba2953d](https://github.com/anthropics/anthropic-sdk-python/commit/ba2953dcaa8a8fcebaa7e8891304687c95b17499)) +* **internal:** fix typos ([#287](https://github.com/anthropics/anthropic-sdk-python/issues/287)) ([4ffbcdf](https://github.com/anthropics/anthropic-sdk-python/commit/4ffbcdf1d3c8c2fbaf7152d207b24cdb0ea82ac9)) +* **internal:** use ruff instead of black for formatting ([#294](https://github.com/anthropics/anthropic-sdk-python/issues/294)) ([1753887](https://github.com/anthropics/anthropic-sdk-python/commit/1753887a776f41bdc2d648329cfe6f20c91125e5)) +* **package:** bump minimum typing-extensions to 4.7 ([#290](https://github.com/anthropics/anthropic-sdk-python/issues/290)) ([9ec5c57](https://github.com/anthropics/anthropic-sdk-python/commit/9ec5c57ba9a14a769d540e48755b05a1c190b45b)) + + +### Documentation + +* **messages:** improvements to helpers reference + typos ([#291](https://github.com/anthropics/anthropic-sdk-python/issues/291)) ([d18a895](https://github.com/anthropics/anthropic-sdk-python/commit/d18a895d380fc0c6610443486d73247b0cd97376)) +* **readme:** remove old migration guide ([#289](https://github.com/anthropics/anthropic-sdk-python/issues/289)) ([eec4574](https://github.com/anthropics/anthropic-sdk-python/commit/eec4574f1f6668804c88bda67b901db10400fbc3)) + ## 0.8.0 (2023-12-19) Full Changelog: [v0.7.8...v0.8.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.7.8...v0.8.0) diff --git a/pyproject.toml b/pyproject.toml index 0993fdcc..265ac8e4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "anthropic" -version = "0.8.0" +version = "0.8.1" description = "The official Python library for the anthropic API" readme = "README.md" license = "MIT" diff --git a/src/anthropic/_version.py b/src/anthropic/_version.py index 810a1124..9703c64f 100644 --- a/src/anthropic/_version.py +++ b/src/anthropic/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. __title__ = "anthropic" -__version__ = "0.8.0" # x-release-please-version +__version__ = "0.8.1" # x-release-please-version