typecast-python 0.3.7__py3-none-any.whl → 0.3.8__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.
typecast/__init__.py CHANGED
@@ -19,6 +19,7 @@ from .models import (
19
19
  OutputStream,
20
20
  PlanTier,
21
21
  Prompt,
22
+ RecommendedVoice,
22
23
  SubscriptionResponse,
23
24
  TTSRequest,
24
25
  TTSRequestStream,
@@ -48,6 +49,7 @@ __all__ = [
48
49
  "OutputStream",
49
50
  "PlanTier",
50
51
  "Prompt",
52
+ "RecommendedVoice",
51
53
  "SubscriptionResponse",
52
54
  "TTSRequest",
53
55
  "TTSRequestStream",
typecast/async_client.py CHANGED
@@ -29,6 +29,7 @@ from .models import (
29
29
  CustomVoice,
30
30
  LanguageCode,
31
31
  Output,
32
+ RecommendedVoice,
32
33
  SubscriptionResponse,
33
34
  TTSModel,
34
35
  TTSPrompt,
@@ -62,34 +63,48 @@ class AsyncTypecast:
62
63
  ... f.write(response.audio_data)
63
64
  """
64
65
 
65
- def __init__(self, host: Optional[str] = None, api_key: Optional[str] = None):
66
+ def __init__(
67
+ self,
68
+ host: Optional[str] = None,
69
+ api_key: Optional[str] = None,
70
+ session: Optional[aiohttp.ClientSession] = None,
71
+ ):
66
72
  """Initialize the async Typecast client.
67
73
 
68
74
  Args:
69
75
  host: API host URL. Defaults to TYPECAST_API_HOST env var
70
76
  or 'https://api.typecast.ai'.
71
77
  api_key: API key for authentication. Defaults to TYPECAST_API_KEY env var.
78
+ session: Optional externally-managed aiohttp.ClientSession. When provided,
79
+ __aenter__ will not create a new session and __aexit__ will not close it
80
+ (the caller owns its lifecycle). Auth headers (`X-API-KEY`, `User-Agent`)
81
+ are attached per-request via `_request_headers()`.
72
82
 
73
83
  Raises:
74
- ValueError: If no API key is provided and TYPECAST_API_KEY is not set.
84
+ ValueError: If no API key is provided and TYPECAST_API_KEY is not set
85
+ for the default host.
75
86
  """
76
87
  self.host = conf.get_host(host)
77
88
  self.api_key = conf.get_api_key(api_key)
78
89
  if not self.api_key and conf.is_default_host(self.host):
79
90
  raise ValueError("API key is required for the default Typecast API host")
80
- self.session: Optional[aiohttp.ClientSession] = None
91
+ self._owns_session = session is None
92
+ self.session: Optional[aiohttp.ClientSession] = session
81
93
 
82
94
  async def __aenter__(self):
83
- # Auth header at session scope; per-request Content-Type is set by aiohttp
84
- # (json= auto-sets application/json, data=FormData() auto-sets multipart).
85
- headers = {"User-Agent": aiohttp_user_agent(self.host)}
86
- if self.api_key:
87
- headers["X-API-KEY"] = self.api_key
88
- self.session = aiohttp.ClientSession(headers=headers)
95
+ # When an external session is injected, do not create a new one.
96
+ # Per-request auth headers are attached via _request_headers() on each call.
97
+ if self.session is None:
98
+ headers = {"User-Agent": aiohttp_user_agent(self.host)}
99
+ if self.api_key:
100
+ headers["X-API-KEY"] = self.api_key
101
+ self.session = aiohttp.ClientSession(headers=headers)
89
102
  return self
90
103
 
91
104
  async def __aexit__(self, exc_type, exc_val, exc_tb):
92
- if self.session:
105
+ # Only close sessions we created. External sessions are caller-owned;
106
+ # leave the reference intact so the client can be re-entered.
107
+ if self.session and self._owns_session:
93
108
  await self.session.close()
94
109
 
95
110
  def _handle_error(self, status_code: int, response_text: str):
@@ -114,6 +129,20 @@ class AsyncTypecast:
114
129
  status_code=status_code,
115
130
  )
116
131
 
132
+ def _request_headers(self) -> Optional[dict]:
133
+ """Headers to attach to each individual request.
134
+
135
+ For owned sessions, auth is set at session scope, so return None and let
136
+ aiohttp use the session headers. For external sessions, the session has
137
+ no auth headers, so we attach X-API-KEY and User-Agent per-request.
138
+ """
139
+ if self._owns_session:
140
+ return None
141
+ headers = {"User-Agent": aiohttp_user_agent(self.host)}
142
+ if self.api_key:
143
+ headers["X-API-KEY"] = self.api_key
144
+ return headers
145
+
117
146
  async def text_to_speech(self, request: TTSRequest) -> TTSResponse:
118
147
  """Convert text to speech asynchronously.
119
148
 
@@ -135,7 +164,9 @@ class AsyncTypecast:
135
164
  raise TypecastError("Client session not initialized. Use async with.")
136
165
  endpoint = "/v1/text-to-speech"
137
166
  async with self.session.post(
138
- f"{self.host}{endpoint}", json=request.model_dump(exclude_none=True)
167
+ f"{self.host}{endpoint}",
168
+ json=request.model_dump(exclude_none=True),
169
+ headers=self._request_headers(),
139
170
  ) as response:
140
171
  if response.status != 200:
141
172
  error_text = await response.text()
@@ -218,6 +249,7 @@ class AsyncTypecast:
218
249
  f"{self.host}{endpoint}",
219
250
  json=request.model_dump(exclude_none=True),
220
251
  timeout=stream_timeout,
252
+ headers=self._request_headers(),
221
253
  ) as response:
222
254
  if response.status != 200:
223
255
  error_text = await response.text()
@@ -261,6 +293,7 @@ class AsyncTypecast:
261
293
  f"{self.host}{endpoint}",
262
294
  json=request.model_dump(exclude_none=True),
263
295
  params=params,
296
+ headers=self._request_headers(),
264
297
  ) as response:
265
298
  if response.status != 200:
266
299
  text = await response.text()
@@ -310,6 +343,7 @@ class AsyncTypecast:
310
343
  f"{self.host}/v1/voices/clone",
311
344
  data=form,
312
345
  timeout=timeout,
346
+ headers=self._request_headers(),
313
347
  ) as response:
314
348
  if response.status != 200:
315
349
  text = await response.text()
@@ -334,6 +368,7 @@ class AsyncTypecast:
334
368
  async with self.session.delete(
335
369
  f"{self.host}/v1/voices/{quote(voice_id, safe='')}",
336
370
  timeout=timeout,
371
+ headers=self._request_headers(),
337
372
  ) as response:
338
373
  if response.status not in (200, 204):
339
374
  text = await response.text()
@@ -360,7 +395,9 @@ class AsyncTypecast:
360
395
  params["model"] = model
361
396
 
362
397
  async with self.session.get(
363
- f"{self.host}{endpoint}", params=params
398
+ f"{self.host}{endpoint}",
399
+ params=params,
400
+ headers=self._request_headers(),
364
401
  ) as response:
365
402
  if response.status != 200:
366
403
  error_text = await response.text()
@@ -388,7 +425,9 @@ class AsyncTypecast:
388
425
  raise TypecastError("Client session not initialized. Use async with.")
389
426
  endpoint = f"/v1/voices/{voice_id}"
390
427
 
391
- async with self.session.get(f"{self.host}{endpoint}") as response:
428
+ async with self.session.get(
429
+ f"{self.host}{endpoint}", headers=self._request_headers()
430
+ ) as response:
392
431
  if response.status != 200:
393
432
  error_text = await response.text()
394
433
  self._handle_error(response.status, error_text)
@@ -425,7 +464,9 @@ class AsyncTypecast:
425
464
  params[key] = getattr(value, "value", value)
426
465
 
427
466
  async with self.session.get(
428
- f"{self.host}{endpoint}", params=params
467
+ f"{self.host}{endpoint}",
468
+ params=params,
469
+ headers=self._request_headers(),
429
470
  ) as response:
430
471
  if response.status != 200:
431
472
  error_text = await response.text()
@@ -452,7 +493,9 @@ class AsyncTypecast:
452
493
  if not self.session:
453
494
  raise TypecastError("Client session not initialized. Use async with.")
454
495
  endpoint = "/v1/users/me/subscription"
455
- async with self.session.get(f"{self.host}{endpoint}") as response:
496
+ async with self.session.get(
497
+ f"{self.host}{endpoint}", headers=self._request_headers()
498
+ ) as response:
456
499
  if response.status != 200:
457
500
  error_text = await response.text()
458
501
  self._handle_error(response.status, error_text)
@@ -475,10 +518,37 @@ class AsyncTypecast:
475
518
  raise TypecastError("Client session not initialized. Use async with.")
476
519
  endpoint = f"/v2/voices/{voice_id}"
477
520
 
478
- async with self.session.get(f"{self.host}{endpoint}") as response:
521
+ async with self.session.get(
522
+ f"{self.host}{endpoint}", headers=self._request_headers()
523
+ ) as response:
479
524
  if response.status != 200:
480
525
  error_text = await response.text()
481
526
  self._handle_error(response.status, error_text)
482
527
 
483
528
  data = await response.json()
484
529
  return VoiceV2Response.model_validate(data)
530
+
531
+ async def recommend_voices(
532
+ self, query: str, count: int = 5
533
+ ) -> list[RecommendedVoice]:
534
+ """Recommend voices from a text description.
535
+
536
+ Recommendation results only include ``voice_id``, ``voice_name``, and
537
+ ``score``. Use ``voice_v2`` or ``voices_v2`` to fetch detailed metadata
538
+ for returned voice IDs.
539
+ """
540
+ if count < 1 or count > 10:
541
+ raise ValueError("count must be between 1 and 10")
542
+ if not self.session:
543
+ raise TypecastError("Client session not initialized. Use async with.")
544
+
545
+ async with self.session.get(
546
+ f"{self.host}/v1/voices/recommendations",
547
+ params={"query": query, "count": count},
548
+ ) as response:
549
+ if response.status != 200:
550
+ error_text = await response.text()
551
+ self._handle_error(response.status, error_text)
552
+
553
+ data = await response.json()
554
+ return [RecommendedVoice.model_validate(item) for item in data]
typecast/client.py CHANGED
@@ -26,6 +26,7 @@ from .models import (
26
26
  CustomVoice,
27
27
  LanguageCode,
28
28
  Output,
29
+ RecommendedVoice,
29
30
  SubscriptionResponse,
30
31
  TTSModel,
31
32
  TTSPrompt,
@@ -96,13 +97,22 @@ class Typecast:
96
97
  ... f.write(response.audio_data)
97
98
  """
98
99
 
99
- def __init__(self, host: Optional[str] = None, api_key: Optional[str] = None):
100
+ def __init__(
101
+ self,
102
+ host: Optional[str] = None,
103
+ api_key: Optional[str] = None,
104
+ session: Optional[requests.Session] = None,
105
+ ):
100
106
  """Initialize the Typecast client.
101
107
 
102
108
  Args:
103
109
  host: API host URL. Defaults to TYPECAST_API_HOST env var
104
110
  or 'https://api.typecast.ai'.
105
111
  api_key: API key for authentication. Defaults to TYPECAST_API_KEY env var.
112
+ session: Optional externally-managed requests.Session. When provided,
113
+ the client will not create a new session nor close it; auth headers
114
+ (`X-API-KEY`, `User-Agent`) are attached per-request via
115
+ `_request_headers()`.
106
116
 
107
117
  Raises:
108
118
  ValueError: If no API key is provided and TYPECAST_API_KEY is not set.
@@ -111,14 +121,18 @@ class Typecast:
111
121
  self.api_key = conf.get_api_key(api_key)
112
122
  if not self.api_key and conf.is_default_host(self.host):
113
123
  raise ValueError("API key is required for the default Typecast API host")
114
- self.session = requests.Session()
115
- headers = {
116
- "Content-Type": "application/json",
117
- "User-Agent": requests_user_agent(self.host),
118
- }
119
- if self.api_key:
120
- headers["X-API-KEY"] = self.api_key
121
- self.session.headers.update(headers)
124
+ self._owns_session = session is None
125
+ if session is not None:
126
+ self.session = session
127
+ else:
128
+ self.session = requests.Session()
129
+ headers = {
130
+ "Content-Type": "application/json",
131
+ "User-Agent": requests_user_agent(self.host),
132
+ }
133
+ if self.api_key:
134
+ headers["X-API-KEY"] = self.api_key
135
+ self.session.headers.update(headers)
122
136
 
123
137
  def _handle_error(self, status_code: int, response_text: str):
124
138
  """Handle HTTP error responses with specific exception types."""
@@ -142,6 +156,20 @@ class Typecast:
142
156
  status_code=status_code,
143
157
  )
144
158
 
159
+ def _request_headers(self) -> Optional[dict]:
160
+ """Headers to attach to each individual request.
161
+
162
+ For owned sessions, auth is set at session scope, so return None and let
163
+ requests use the session headers. For external sessions, the session has
164
+ no auth headers, so we attach X-API-KEY and User-Agent per-request.
165
+ """
166
+ if self._owns_session:
167
+ return None
168
+ headers = {"User-Agent": requests_user_agent(self.host)}
169
+ if self.api_key:
170
+ headers["X-API-KEY"] = self.api_key
171
+ return headers
172
+
145
173
  def text_to_speech(self, request: TTSRequest) -> TTSResponse:
146
174
  """Convert text to speech.
147
175
 
@@ -160,7 +188,9 @@ class Typecast:
160
188
  """
161
189
  endpoint = "/v1/text-to-speech"
162
190
  response = self.session.post(
163
- f"{self.host}{endpoint}", json=request.model_dump(exclude_none=True)
191
+ f"{self.host}{endpoint}",
192
+ json=request.model_dump(exclude_none=True),
193
+ headers=self._request_headers(),
164
194
  )
165
195
  if response.status_code != 200:
166
196
  self._handle_error(response.status_code, response.text)
@@ -252,6 +282,7 @@ class Typecast:
252
282
  json=request.model_dump(exclude_none=True),
253
283
  stream=True,
254
284
  timeout=(10, 300),
285
+ headers=self._request_headers(),
255
286
  )
256
287
  if response.status_code != 200:
257
288
  error_text = response.text
@@ -299,6 +330,7 @@ class Typecast:
299
330
  json=request.model_dump(exclude_none=True),
300
331
  params=params,
301
332
  timeout=(10, 300),
333
+ headers=self._request_headers(),
302
334
  )
303
335
  if response.status_code != 200:
304
336
  self._handle_error(response.status_code, response.text)
@@ -335,12 +367,19 @@ class Typecast:
335
367
  }
336
368
  data = {"name": name, "model": model_str}
337
369
  # Remove the session-level Content-Type so requests can set the
338
- # correct multipart/form-data boundary for this request.
370
+ # correct multipart/form-data boundary for this request. For external
371
+ # sessions, also attach the per-request auth headers (X-API-KEY,
372
+ # User-Agent); _request_headers() returns None for owned sessions, so
373
+ # owned-session behavior is unchanged.
374
+ headers = {"Content-Type": None}
375
+ per_request = self._request_headers()
376
+ if per_request:
377
+ headers.update(per_request)
339
378
  response = self.session.post(
340
379
  f"{self.host}/v1/voices/clone",
341
380
  files=files,
342
381
  data=data,
343
- headers={"Content-Type": None},
382
+ headers=headers,
344
383
  timeout=(10, 300),
345
384
  )
346
385
  if response.status_code != 200:
@@ -361,6 +400,7 @@ class Typecast:
361
400
  response = self.session.delete(
362
401
  f"{self.host}/v1/voices/{quote(voice_id, safe='')}",
363
402
  timeout=(10, 60),
403
+ headers=self._request_headers(),
364
404
  )
365
405
  if response.status_code not in (200, 204):
366
406
  self._handle_error(response.status_code, response.text)
@@ -383,7 +423,9 @@ class Typecast:
383
423
  if model:
384
424
  params["model"] = model
385
425
 
386
- response = self.session.get(f"{self.host}{endpoint}", params=params)
426
+ response = self.session.get(
427
+ f"{self.host}{endpoint}", params=params, headers=self._request_headers()
428
+ )
387
429
 
388
430
  if response.status_code != 200:
389
431
  self._handle_error(response.status_code, response.text)
@@ -406,7 +448,9 @@ class Typecast:
406
448
  This method is deprecated. Use voices_v2() for enhanced metadata.
407
449
  """
408
450
  endpoint = f"/v1/voices/{voice_id}"
409
- response = self.session.get(f"{self.host}{endpoint}")
451
+ response = self.session.get(
452
+ f"{self.host}{endpoint}", headers=self._request_headers()
453
+ )
410
454
 
411
455
  if response.status_code != 200:
412
456
  self._handle_error(response.status_code, response.text)
@@ -440,7 +484,9 @@ class Typecast:
440
484
  for key, value in filter_dict.items():
441
485
  params[key] = getattr(value, "value", value)
442
486
 
443
- response = self.session.get(f"{self.host}{endpoint}", params=params)
487
+ response = self.session.get(
488
+ f"{self.host}{endpoint}", params=params, headers=self._request_headers()
489
+ )
444
490
 
445
491
  if response.status_code != 200:
446
492
  self._handle_error(response.status_code, response.text)
@@ -462,7 +508,9 @@ class Typecast:
462
508
  InternalServerError: On server-side failures.
463
509
  """
464
510
  endpoint = "/v1/users/me/subscription"
465
- response = self.session.get(f"{self.host}{endpoint}")
511
+ response = self.session.get(
512
+ f"{self.host}{endpoint}", headers=self._request_headers()
513
+ )
466
514
  if response.status_code != 200:
467
515
  self._handle_error(response.status_code, response.text)
468
516
  return SubscriptionResponse.model_validate(response.json())
@@ -480,9 +528,33 @@ class Typecast:
480
528
  NotFoundError: If the voice ID does not exist.
481
529
  """
482
530
  endpoint = f"/v2/voices/{voice_id}"
483
- response = self.session.get(f"{self.host}{endpoint}")
531
+ response = self.session.get(
532
+ f"{self.host}{endpoint}", headers=self._request_headers()
533
+ )
484
534
 
485
535
  if response.status_code != 200:
486
536
  self._handle_error(response.status_code, response.text)
487
537
 
488
538
  return VoiceV2Response.model_validate(response.json())
539
+
540
+ def recommend_voices(self, query: str, count: int = 5) -> list[RecommendedVoice]:
541
+ """Recommend voices from a text description.
542
+
543
+ Recommendation results only include ``voice_id``, ``voice_name``, and
544
+ ``score``. Use ``voice_v2`` or ``voices_v2`` to fetch detailed metadata
545
+ for returned voice IDs.
546
+
547
+ Args:
548
+ query: Text description of the desired voice.
549
+ count: Number of recommendations to return, from 1 to 10. Defaults to 5.
550
+ """
551
+ if count < 1 or count > 10:
552
+ raise ValueError("count must be between 1 and 10")
553
+
554
+ response = self.session.get(
555
+ f"{self.host}/v1/voices/recommendations",
556
+ params={"query": query, "count": count},
557
+ )
558
+ if response.status_code != 200:
559
+ self._handle_error(response.status_code, response.text)
560
+ return [RecommendedVoice.model_validate(item) for item in response.json()]
@@ -23,6 +23,7 @@ from .voices import (
23
23
  CustomVoice,
24
24
  GenderEnum,
25
25
  ModelInfo,
26
+ RecommendedVoice,
26
27
  UseCaseEnum,
27
28
  VoicesResponse,
28
29
  VoicesV2Filter,
@@ -46,6 +47,7 @@ __all__ = [
46
47
  "PlanTier",
47
48
  "Prompt",
48
49
  "PresetPrompt",
50
+ "RecommendedVoice",
49
51
  "SmartPrompt",
50
52
  "SubscriptionResponse",
51
53
  "TTSModel",
typecast/models/voices.py CHANGED
@@ -68,6 +68,19 @@ class VoiceV2Response(BaseModel):
68
68
  use_cases: Optional[list[str]] = None
69
69
 
70
70
 
71
+ class RecommendedVoice(BaseModel):
72
+ """Recommended voice result.
73
+
74
+ Recommendation results only include the matched voice ID, voice name, and
75
+ similarity score. Use ``voice_v2`` or ``voices_v2`` to fetch detailed voice
76
+ metadata for a returned ``voice_id``.
77
+ """
78
+
79
+ voice_id: str
80
+ voice_name: str
81
+ score: float
82
+
83
+
71
84
  class VoicesV2Filter(BaseModel):
72
85
  """Filter options for V2 voices endpoint"""
73
86
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: typecast-python
3
- Version: 0.3.7
3
+ Version: 0.3.8
4
4
  Summary: Official Typecast Python SDK - Convert text to lifelike speech using AI-powered voices
5
5
  Project-URL: Homepage, https://typecast.ai
6
6
  Project-URL: Documentation, https://typecast.ai/docs/overview
@@ -1,18 +1,18 @@
1
- typecast/__init__.py,sha256=3pdJqNkXCZ7svzqab4sBR_qwyoM5E2sPjfuci1g1Ub8,1047
1
+ typecast/__init__.py,sha256=L6m0Z1jn1UgGjPp9AFiTwhHk9DVV_B4WrLfQDEfbcZI,1093
2
2
  typecast/_user_agent.py,sha256=R_0eSKpuP9jLp_wDJcJGFvFtUh9BZwEgsT1buVeHRAA,1786
3
3
  typecast/_voice_clone.py,sha256=TN2tbB3b5lC5uFCBnbERhz37bNJlbv6VZ-vj70EfTs4,3464
4
- typecast/async_client.py,sha256=65dx24vxiZGpNAGMzxr_7CsG51qZMNAraCuzKjW0e-E,18746
5
- typecast/client.py,sha256=jFts3z0vmtGb32rGJ8_Wcswu6mo6oXVNzdW9MD3GqGE,17974
4
+ typecast/async_client.py,sha256=H3hCoGb6h0UyrnYUlQ0lhnpaCV-hxnZtsQKYlqaGPSk,21592
5
+ typecast/client.py,sha256=VPZVRnB9_T4VkSlrHpKLRahhEaxIF05XUAk-skqYKz8,20850
6
6
  typecast/composer.py,sha256=r1kpRGlSJ1s4C1PkE7yIpGwnKtxYfIdFSLWaf8v1RAQ,9422
7
7
  typecast/conf.py,sha256=yfOHYZDvIshWWMriN5wE0GFPR8AX3zv4Rg2655OvA9Q,724
8
8
  typecast/exceptions.py,sha256=Y0ZzYebe8zOSOSAHbXfKR0G_RJgdmZXxi15Z7ZxPLIk,1568
9
9
  typecast/utils.py,sha256=XuNuX7gW8_CGKqZ-cv_tKlPVMPBluAYJBw2clwmjIMI,708
10
- typecast/models/__init__.py,sha256=UEPUjg86fpCMXUvAzcNgxSPPhuPwCY9aQbzK3w90Fj0,1203
10
+ typecast/models/__init__.py,sha256=OmaFdGpH6gK7EJyNgvbwqZHXVxjnWKJzynn7D3pfULo,1249
11
11
  typecast/models/error.py,sha256=XomIjx7jvlCjItqzJuCAT4mXC9jwTjxR8lLDUk6P8KA,152
12
12
  typecast/models/subscription.py,sha256=EIaAAo3cCRw8LYT_O6D9AVwxqIHrWCijzl4UTx7FZB8,894
13
13
  typecast/models/tts.py,sha256=W2Bp9M-xPJPODlk89P9CnLA2fwb__4SR4yoMkG_O8kA,16453
14
- typecast/models/voices.py,sha256=-EXP35jDy7_G30k5bDnVrFJHp6svEDTA5jJ8oHAgXNQ,2310
15
- typecast_python-0.3.7.dist-info/METADATA,sha256=louuBNIHV_BGlSbchFiyXEaxSW4B-NxyH4AThzJqSAc,25530
16
- typecast_python-0.3.7.dist-info/WHEEL,sha256=mffPy8wBnZQn2VnJUU5jE99KsxaSfiyMHV9Yt0aLVxs,87
17
- typecast_python-0.3.7.dist-info/licenses/LICENSE,sha256=HvtJ-S89uUkuYmt-OvVk4MRxmzwtbn84__qJtSrGU2Q,11348
18
- typecast_python-0.3.7.dist-info/RECORD,,
14
+ typecast/models/voices.py,sha256=3yJD78QbZe3pTZv61GnB8up8qlIHKd1iSe5Bow-4ocU,2645
15
+ typecast_python-0.3.8.dist-info/METADATA,sha256=5Gbl-QxbRCGnqUwn_8Yr2xssRMZD_3GvZghgWsUxByg,25530
16
+ typecast_python-0.3.8.dist-info/WHEEL,sha256=mffPy8wBnZQn2VnJUU5jE99KsxaSfiyMHV9Yt0aLVxs,87
17
+ typecast_python-0.3.8.dist-info/licenses/LICENSE,sha256=HvtJ-S89uUkuYmt-OvVk4MRxmzwtbn84__qJtSrGU2Q,11348
18
+ typecast_python-0.3.8.dist-info/RECORD,,