33import json
44import logging
55import os
6- from typing import Any , Dict , List , Optional , Tuple , Union
6+ from typing import Any , Dict , List , Optional , Tuple , Union , TYPE_CHECKING
77
88import requests
99
1010from . import config
1111
12+ if TYPE_CHECKING :
13+ from _typeshed import SupportsWrite
14+
1215LOG = logging .getLogger (__name__ )
1316
1417Record = Union [Tuple , List [Any ]]
1518Records = List [Record ]
1619
1720
1821def to_csv (records : Records , ** kwargs ) -> str :
22+ """
23+ Convert the given records to CSV using a CSV writer, and return them as a single string.
24+
25+ :param records: The records to convert to CSV.
26+ :param kwargs: Args to be passed to ``csv.writer``.
27+ :return: A string representing the CSV
28+ """
1929 output = io .StringIO ()
2030 write_csv (output , records , ** kwargs )
2131 return output .getvalue ()
2232
2333
24- def write_csv (file , records : Records , ** kwargs ):
34+ def write_csv (file : "SupportsWrite[str]" , records : Records , ** kwargs ):
35+ """
36+ Converts the given records to CSV and writes them to the given file.
37+
38+ :param file: The file passed to the CSV writer.
39+ :param records: The records to convert to CSV.
40+ :param kwargs: Args to be passed to ``csv.writer``.
41+ """
2542 # TODO: do proper type conversion here to optimize for CSV input
2643 # see: https://guides.tinybird.co/guide/fine-tuning-csvs-for-fast-ingestion
2744
@@ -45,24 +62,39 @@ class Datasource:
4562 name : str
4663 version : Optional [int ]
4764
48- def __init__ (self , name , token , version : int = None , api = None ) -> None :
65+ def __init__ (self , name , token , version : int = None , api : str = None ) -> None :
4966 self .name = name
5067 self .token = token
5168 self .version = version
5269 self .api = (api or config .API_URL ).rstrip ("/" ) + self .endpoint
5370
5471 @property
55- def canonical_name (self ):
72+ def canonical_name (self ) -> str :
73+ """
74+ Returns the name of the table that can be queried. If a version is specified, the name will be suffixed with
75+ ``__v<version>``. Otherwise, this just returns the name. Note that versions are discouraged in the current
76+ tinybird workflows.
77+
78+ :return: The canonical name of the table that can be used in queries
79+ """
5680 if self .version is not None :
5781 return f"{ self .name } __v{ self .version } "
5882 else :
5983 return self .name
6084
61- def append (self , records : List [Record ], * args , ** kwargs ) -> requests .Response :
85+ def append (self , records : Records , * args , ** kwargs ) -> requests .Response :
86+ """Calls ``append_csv``."""
6287 # TODO: replicate tinybird API concepts instead of returning Response
6388 return self .append_csv (records , * args , ** kwargs )
6489
65- def append_csv (self , records : List [Record ], delimiter : str = "," ) -> requests .Response :
90+ def append_csv (self , records : Records , delimiter : str = "," ) -> requests .Response :
91+ """
92+ Makes a POST request to the datasource using mode=append with CSV data. This appends data to the table.
93+
94+ :param records: List of records to append. The will be converted to CSV using the provided delimiter.
95+ :param delimiter: Optional delimiter (defaults to ",")
96+ :return: The HTTP response
97+ """
6698 params = {"name" : self .canonical_name , "mode" : "append" }
6799 if delimiter :
68100 params ["dialect_delimiter" ] = delimiter
@@ -84,6 +116,12 @@ def append_csv(self, records: List[Record], delimiter: str = ",") -> requests.Re
84116 return requests .post (url = self .api , params = params , headers = headers , data = data )
85117
86118 def append_ndjson (self , records : List [Dict ]) -> requests .Response :
119+ """
120+ Makes a POST request to the datasource using mode=append with ndjson data. This appends data to the table.
121+
122+ :param records: List of JSON records to append. The will be converted to NDJSON using ``json.dumps``
123+ :return: The HTTP response
124+ """
87125 # TODO: generalize appending in different formats
88126 query = {"name" : self .canonical_name , "mode" : "append" , "format" : "ndjson" }
89127
@@ -130,14 +168,16 @@ def __repr__(self):
130168
131169
132170class FileDatasource (Datasource ):
133- # for debugging/development purposes
171+ """
172+ Datasource that writes into a file, used for testing and development purposes.
173+ """
134174
135175 def __init__ (self , path : str ):
136176 name = os .path .basename (path )
137177 super ().__init__ (name , None )
138178 self .path = path
139179
140- def append (self , records : Records ) -> requests .Response :
180+ def append_csv (self , records : Records , * args , ** kwargs ) -> requests .Response :
141181 if records :
142182 with open (self .path , "a" ) as fd :
143183 write_csv (fd , records )
@@ -152,3 +192,6 @@ def append_ndjson(self, records: List[Dict]) -> requests.Response:
152192 def readlines (self ) -> List [str ]:
153193 with open (self .path , "r" ) as fd :
154194 return fd .readlines ()
195+
196+ def truncate (self ):
197+ raise NotImplementedError
0 commit comments