csc-cia-stne 0.0.88__py3-none-any.whl → 0.0.90__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.
- csc_cia_stne/__init__.py +5 -1
- csc_cia_stne/gcp_bigquery.py +4 -11
- csc_cia_stne/gcp_bucket.py +293 -0
- csc_cia_stne/utilitarios/validations/gcp_bucket.py +152 -0
- csc_cia_stne/utilitarios/validations/waccess.py +215 -0
- csc_cia_stne/wacess.py +836 -0
- {csc_cia_stne-0.0.88.dist-info → csc_cia_stne-0.0.90.dist-info}/METADATA +1 -1
- {csc_cia_stne-0.0.88.dist-info → csc_cia_stne-0.0.90.dist-info}/RECORD +11 -7
- {csc_cia_stne-0.0.88.dist-info → csc_cia_stne-0.0.90.dist-info}/WHEEL +1 -1
- {csc_cia_stne-0.0.88.dist-info → csc_cia_stne-0.0.90.dist-info}/licenses/LICENCE +0 -0
- {csc_cia_stne-0.0.88.dist-info → csc_cia_stne-0.0.90.dist-info}/top_level.txt +0 -0
csc_cia_stne/__init__.py
CHANGED
@@ -13,6 +13,8 @@ from .provio import Provio
|
|
13
13
|
from .google_drive import GoogleDrive
|
14
14
|
from .slack import Slack
|
15
15
|
from .web import web_screen
|
16
|
+
from .wacess import Waccess
|
17
|
+
from .gcp_bucket import GCPBucket
|
16
18
|
|
17
19
|
# Define os itens disponíveis para importação
|
18
20
|
__all__ = [
|
@@ -28,7 +30,9 @@ __all__ = [
|
|
28
30
|
"Email",
|
29
31
|
"GoogleDrive",
|
30
32
|
"Slack",
|
31
|
-
"web_screen"
|
33
|
+
"web_screen",
|
34
|
+
"Waccess",
|
35
|
+
"GCPBucket"
|
32
36
|
]
|
33
37
|
|
34
38
|
_diretorio_inicial = os.getcwd()
|
csc_cia_stne/gcp_bigquery.py
CHANGED
@@ -66,20 +66,13 @@ class BigQuery():
|
|
66
66
|
|
67
67
|
else:
|
68
68
|
|
69
|
-
|
70
|
-
|
71
|
-
'error':"Credenciais não fornecidas"
|
72
|
-
}
|
73
|
-
|
69
|
+
raise ValueError("Credenciais não fornecidas")
|
70
|
+
|
74
71
|
return client
|
75
72
|
|
76
73
|
except Exception as e:
|
77
|
-
|
78
|
-
|
79
|
-
'status':False,
|
80
|
-
'error':'Problema ao tentar gerar o client do big query',
|
81
|
-
'details':str(e)
|
82
|
-
}
|
74
|
+
|
75
|
+
raise ValueError("Erro ao criar o cliente BigQuery:", e)
|
83
76
|
|
84
77
|
|
85
78
|
def try_query(self, query_to_execute: str, organize: bool = False, use_legacy: bool = False, use_cache: bool = False, query_parameters: list = []) -> dict:
|
@@ -0,0 +1,293 @@
|
|
1
|
+
from google.oauth2 import service_account
|
2
|
+
from google.cloud import storage
|
3
|
+
from pydantic import ValidationError
|
4
|
+
|
5
|
+
from .utilitarios.validations.gcp_bucket import InitParamsValidator,ListFilesValidator,GetFilesValidator,UploadFilesValidator,DeleteFilesValidator
|
6
|
+
|
7
|
+
|
8
|
+
class GCPBucket():
|
9
|
+
|
10
|
+
def __init__(self, creds_dict: dict = None, creds_file: str = None):
|
11
|
+
"""
|
12
|
+
Inicializa uma instância da classe com as credenciais fornecidas.
|
13
|
+
Args:
|
14
|
+
creds_dict (dict, opcional): Um dicionário contendo as credenciais para autenticação.
|
15
|
+
creds_file (str, opcional): O caminho para um arquivo contendo as credenciais para autenticação.
|
16
|
+
Raises:
|
17
|
+
ValueError: Se os dados de entrada para inicialização da instância não forem válidos.
|
18
|
+
"""
|
19
|
+
|
20
|
+
try:
|
21
|
+
|
22
|
+
InitParamsValidator(creds_dict=creds_dict, creds_file=creds_file)
|
23
|
+
|
24
|
+
except ValidationError as e:
|
25
|
+
|
26
|
+
raise ValueError("Erro na validação dos dados de input da inicialização da instância:", e.errors())
|
27
|
+
|
28
|
+
self.creds_dict = creds_dict
|
29
|
+
self.creds_file = creds_file
|
30
|
+
self.client = self._create_client()
|
31
|
+
|
32
|
+
def _create_client(self):
|
33
|
+
"""
|
34
|
+
Cria um cliente para interagir com o Google Cloud Storage.
|
35
|
+
Este método utiliza as credenciais fornecidas para autenticar e criar
|
36
|
+
um cliente do Google Cloud Storage. As credenciais podem ser fornecidas
|
37
|
+
como um dicionário (`creds_dict`) ou como um arquivo (`creds_file`).
|
38
|
+
Raises:
|
39
|
+
ValueError: Se nenhum arquivo de credenciais ou dicionário de credenciais
|
40
|
+
for fornecido, ou se ocorrer um erro ao criar o cliente do bucket.
|
41
|
+
Returns:
|
42
|
+
storage.Client: Uma instância autenticada do cliente do Google Cloud Storage.
|
43
|
+
"""
|
44
|
+
|
45
|
+
try:
|
46
|
+
|
47
|
+
if self.creds_dict is not None:
|
48
|
+
|
49
|
+
credentials = service_account.Credentials.from_service_account_info(
|
50
|
+
self.creds_dict,
|
51
|
+
)
|
52
|
+
|
53
|
+
elif self.creds_file is not None:
|
54
|
+
|
55
|
+
credentials = service_account.Credentials.from_service_account_file(
|
56
|
+
self.creds_file,
|
57
|
+
)
|
58
|
+
|
59
|
+
else:
|
60
|
+
|
61
|
+
raise ValueError("Nenhum arquivo de credenciais ou dicionário de credenciais fornecido.")
|
62
|
+
|
63
|
+
return storage.Client(credentials=credentials)
|
64
|
+
|
65
|
+
except Exception as e:
|
66
|
+
|
67
|
+
raise ValueError("Erro ao criar o cliente do bucket:", e)
|
68
|
+
|
69
|
+
# Função para listar os arquivos em um bucket
|
70
|
+
def list_files(self, bucket_name:str) -> dict:
|
71
|
+
"""
|
72
|
+
Lista os arquivos presentes em um bucket do Google Cloud Storage.
|
73
|
+
Args:
|
74
|
+
bucket_name (str): O nome do bucket do qual os arquivos serão listados.
|
75
|
+
Returns:
|
76
|
+
dict: Um dicionário contendo:
|
77
|
+
- 'success' (bool): Indica se a operação foi bem-sucedida.
|
78
|
+
- 'files' (list): Uma lista com os nomes dos arquivos no bucket (presente apenas se 'success' for True).
|
79
|
+
- 'error' (str): Mensagem de erro (presente apenas se 'success' for False).
|
80
|
+
- 'details' (str): Detalhes adicionais sobre o erro (presente apenas se 'success' for False).
|
81
|
+
Raises:
|
82
|
+
ValueError: Se os dados de entrada não forem válidos, contendo detalhes do erro de validação.
|
83
|
+
"""
|
84
|
+
|
85
|
+
try:
|
86
|
+
|
87
|
+
ListFilesValidator(bucket_name=bucket_name)
|
88
|
+
|
89
|
+
except ValidationError as e:
|
90
|
+
|
91
|
+
raise ValueError("Erro na validação dos dados de input da função de listagem de arquivos", e.errors())
|
92
|
+
|
93
|
+
try:
|
94
|
+
|
95
|
+
bucket = self.client.bucket(bucket_name)
|
96
|
+
|
97
|
+
blobs = bucket.list_blobs()
|
98
|
+
|
99
|
+
files = [blob.name for blob in blobs]
|
100
|
+
|
101
|
+
return {
|
102
|
+
'success':True,
|
103
|
+
'files':files
|
104
|
+
}
|
105
|
+
|
106
|
+
except Exception as e:
|
107
|
+
|
108
|
+
return {
|
109
|
+
'success':False,
|
110
|
+
'error':str(e),
|
111
|
+
'details':'Erro ao acessar o bucket'
|
112
|
+
}
|
113
|
+
|
114
|
+
# Função para baixar os arquivos de um bucket
|
115
|
+
def get_file(self, bucket_name:str, filename:str, destination:str=None, chunksize:int=256) -> dict:
|
116
|
+
"""
|
117
|
+
Faz o download de um arquivo de um bucket do Google Cloud Storage para um destino local.
|
118
|
+
Args:
|
119
|
+
bucket_name (str): Nome do bucket no Google Cloud Storage.
|
120
|
+
filename (str): Nome do arquivo a ser baixado do bucket.
|
121
|
+
destination (str, opcional): Caminho completo onde o arquivo será salvo localmente.
|
122
|
+
Se não for especificado, o arquivo será salvo com o mesmo nome do arquivo no bucket.
|
123
|
+
chunksize (int, opcional): Tamanho do chunk em megabytes para o download do arquivo.
|
124
|
+
O padrão é 256 MB.
|
125
|
+
Returns:
|
126
|
+
dict: Um dicionário contendo o status da operação.
|
127
|
+
- 'success' (bool): Indica se a operação foi bem-sucedida.
|
128
|
+
- 'error' (str, opcional): Mensagem de erro, caso ocorra uma exceção.
|
129
|
+
- 'details' (str): Detalhes adicionais sobre o sucesso ou falha da operação.
|
130
|
+
Raises:
|
131
|
+
ValueError: Se os dados de entrada não forem válidos.
|
132
|
+
"""
|
133
|
+
|
134
|
+
try:
|
135
|
+
|
136
|
+
GetFilesValidator(bucket_name=bucket_name, filename=filename, destination=destination, chunksize=chunksize)
|
137
|
+
|
138
|
+
except ValidationError as e:
|
139
|
+
|
140
|
+
raise ValueError("Erro na validação dos dados de input da função para obter arquivos", e.errors())
|
141
|
+
|
142
|
+
if destination is None:
|
143
|
+
|
144
|
+
destination = filename
|
145
|
+
|
146
|
+
try:
|
147
|
+
|
148
|
+
bucket = self.client.bucket(bucket_name)
|
149
|
+
|
150
|
+
except Exception as e:
|
151
|
+
|
152
|
+
return {
|
153
|
+
'success':False,
|
154
|
+
'error':str(e),
|
155
|
+
'details':'Erro ao acessar o bucket'
|
156
|
+
}
|
157
|
+
|
158
|
+
try:
|
159
|
+
|
160
|
+
blob = bucket.blob(filename)
|
161
|
+
|
162
|
+
blob.chunk_size = chunksize * 1024 * 1024 # 256M
|
163
|
+
|
164
|
+
blob.download_to_filename(destination)
|
165
|
+
|
166
|
+
return {
|
167
|
+
'success':True,
|
168
|
+
'details':f'Arquivo baixado para o diretório: {str(destination)}'
|
169
|
+
}
|
170
|
+
|
171
|
+
except Exception as e:
|
172
|
+
|
173
|
+
return {
|
174
|
+
'success':False,
|
175
|
+
'error':str(e),
|
176
|
+
'details':'Erro ao baixar o arquivo do bucket'
|
177
|
+
}
|
178
|
+
|
179
|
+
# Função para realizr o upload de arquivos para um bucket
|
180
|
+
def upload_file(self, bucket_name:str, filename:str, destination:str=None) -> dict:
|
181
|
+
"""
|
182
|
+
Faz o upload de um arquivo para um bucket no Google Cloud Storage.
|
183
|
+
Args:
|
184
|
+
bucket_name (str): Nome do bucket onde o arquivo será enviado.
|
185
|
+
filename (str): Caminho do arquivo local que será enviado.
|
186
|
+
destination (str, opcional): Caminho de destino no bucket.
|
187
|
+
Se não for especificado, será utilizado o mesmo nome do arquivo local.
|
188
|
+
Returns:
|
189
|
+
dict: Um dicionário contendo o resultado da operação.
|
190
|
+
- 'success' (bool): Indica se o upload foi bem-sucedido.
|
191
|
+
- 'details' (str): Mensagem detalhada sobre o resultado.
|
192
|
+
- 'error' (str, opcional): Mensagem de erro, caso o upload falhe.
|
193
|
+
Raises:
|
194
|
+
ValueError: Se os dados de entrada não forem válidos.
|
195
|
+
"""
|
196
|
+
|
197
|
+
try:
|
198
|
+
|
199
|
+
UploadFilesValidator(bucket_name=bucket_name, filename=filename, destination=destination)
|
200
|
+
|
201
|
+
except ValidationError as e:
|
202
|
+
|
203
|
+
raise ValueError("Erro na validação dos dados de input da função para realizar upload de arquivos", e.errors())
|
204
|
+
|
205
|
+
if destination is None:
|
206
|
+
|
207
|
+
destination = filename
|
208
|
+
|
209
|
+
try:
|
210
|
+
|
211
|
+
bucket = self.client.bucket(bucket_name)
|
212
|
+
|
213
|
+
except Exception as e:
|
214
|
+
|
215
|
+
return {
|
216
|
+
'success':False,
|
217
|
+
'error':str(e),
|
218
|
+
'details':'Erro ao acessar o bucket'
|
219
|
+
}
|
220
|
+
|
221
|
+
try:
|
222
|
+
|
223
|
+
blob = bucket.blob(destination)
|
224
|
+
|
225
|
+
blob.upload_from_filename(filename)
|
226
|
+
|
227
|
+
return {
|
228
|
+
'success':True,
|
229
|
+
'details':f'Arquivo enviado para o bucket: {str(destination)}'
|
230
|
+
}
|
231
|
+
|
232
|
+
except Exception as e:
|
233
|
+
|
234
|
+
return {
|
235
|
+
'success':False,
|
236
|
+
'error':str(e),
|
237
|
+
'details':'Erro ao enviar o arquivo para o bucket'
|
238
|
+
}
|
239
|
+
|
240
|
+
# Função para deletar arquivos de um bucket
|
241
|
+
def delete_file(self, bucket_name:str, filename:str) -> dict:
|
242
|
+
"""
|
243
|
+
Deleta um arquivo de um bucket no Google Cloud Storage.
|
244
|
+
Args:
|
245
|
+
bucket_name (str): Nome do bucket onde o arquivo está armazenado.
|
246
|
+
filename (str): Nome do arquivo a ser deletado.
|
247
|
+
Returns:
|
248
|
+
dict: Um dicionário contendo o resultado da operação.
|
249
|
+
- 'success' (bool): Indica se a operação foi bem-sucedida.
|
250
|
+
- 'error' (str, opcional): Mensagem de erro, caso a operação falhe.
|
251
|
+
- 'details' (str): Detalhes adicionais sobre o resultado da operação.
|
252
|
+
Raises:
|
253
|
+
ValueError: Caso os dados de entrada não passem na validação.
|
254
|
+
"""
|
255
|
+
|
256
|
+
try:
|
257
|
+
|
258
|
+
DeleteFilesValidator(bucket_name=bucket_name, filename=filename)
|
259
|
+
|
260
|
+
except ValidationError as e:
|
261
|
+
|
262
|
+
raise ValueError("Erro na validação dos dados de input da função para deletar arquivos", e.errors())
|
263
|
+
|
264
|
+
try:
|
265
|
+
|
266
|
+
bucket = self.client.bucket(bucket_name)
|
267
|
+
|
268
|
+
except Exception as e:
|
269
|
+
|
270
|
+
return {
|
271
|
+
'success':False,
|
272
|
+
'error':str(e),
|
273
|
+
'details':'Erro ao acessar o bucket'
|
274
|
+
}
|
275
|
+
|
276
|
+
try:
|
277
|
+
|
278
|
+
blob = bucket.blob(filename)
|
279
|
+
|
280
|
+
blob.delete()
|
281
|
+
|
282
|
+
return {
|
283
|
+
'success':True,
|
284
|
+
'details':f'Arquivo deletado do bucket: {str(filename)}'
|
285
|
+
}
|
286
|
+
|
287
|
+
except Exception as e:
|
288
|
+
|
289
|
+
return {
|
290
|
+
'success':False,
|
291
|
+
'error':str(e),
|
292
|
+
'details':'Erro ao deletar o arquivo do bucket'
|
293
|
+
}
|
@@ -0,0 +1,152 @@
|
|
1
|
+
from pydantic import BaseModel, field_validator, model_validator
|
2
|
+
|
3
|
+
class InitParamsValidator(BaseModel):
|
4
|
+
"""
|
5
|
+
Classe InitParamsValidator
|
6
|
+
Valida os parâmetros de inicialização para garantir que pelo menos um dos
|
7
|
+
parâmetros 'creds_dict' ou 'creds_file' seja fornecido.
|
8
|
+
Atributos:
|
9
|
+
creds_dict (dict): Um dicionário contendo as credenciais.
|
10
|
+
creds_file (str): Um caminho para o arquivo contendo as credenciais.
|
11
|
+
Métodos:
|
12
|
+
check_others_input(cls, model):
|
13
|
+
Valida se pelo menos um dos parâmetros 'creds_dict' ou 'creds_file'
|
14
|
+
foi fornecido. Levanta um ValueError caso ambos estejam ausentes ou
|
15
|
+
inválidos.
|
16
|
+
"""
|
17
|
+
|
18
|
+
creds_dict:dict
|
19
|
+
creds_file:str
|
20
|
+
|
21
|
+
@model_validator(mode="after")
|
22
|
+
def check_others_input(cls, model):
|
23
|
+
creds_dict = model.creds_dict
|
24
|
+
creds_file = model.creds_file
|
25
|
+
|
26
|
+
if isinstance(creds_dict, dict):
|
27
|
+
return model
|
28
|
+
|
29
|
+
elif isinstance(creds_file, str) and creds_file.strip():
|
30
|
+
return model
|
31
|
+
|
32
|
+
else:
|
33
|
+
raise ValueError("Pelo menos um dos parâmetros 'creds_dict' ou 'creds_file' deve ser fornecido.")
|
34
|
+
|
35
|
+
class ListFilesValidator(BaseModel):
|
36
|
+
"""
|
37
|
+
Classe ListFilesValidator
|
38
|
+
Valida os dados relacionados ao nome de um bucket no Google Cloud Platform (GCP).
|
39
|
+
Atributos:
|
40
|
+
bucket_name (str): Nome do bucket que será validado. Deve ser uma string não vazia.
|
41
|
+
Métodos:
|
42
|
+
check_str_name(cls, value, info):
|
43
|
+
Valida se o valor fornecido para o nome do bucket é uma string não vazia.
|
44
|
+
Levanta um ValueError se a validação falhar.
|
45
|
+
"""
|
46
|
+
|
47
|
+
bucket_name:str
|
48
|
+
|
49
|
+
@field_validator("bucket_name")
|
50
|
+
def check_str_name(cls, value, info):
|
51
|
+
if not isinstance(value, str) or not value.strip():
|
52
|
+
raise ValueError("O nome do bucket deve ser uma string não vazia.")
|
53
|
+
return value
|
54
|
+
|
55
|
+
class GetFilesValidator(BaseModel):
|
56
|
+
"""
|
57
|
+
Classe GetFilesValidator
|
58
|
+
Valida os parâmetros necessários para operações relacionadas a arquivos em um bucket GCP.
|
59
|
+
Atributos:
|
60
|
+
bucket_name (str): Nome do bucket GCP. Deve ser uma string não vazia.
|
61
|
+
filename (str): Nome do arquivo. Deve ser uma string não vazia.
|
62
|
+
destination (str): Caminho de destino. Deve ser uma string.
|
63
|
+
chunksize (int): Tamanho dos chunks para processamento. Deve ser um inteiro.
|
64
|
+
Métodos:
|
65
|
+
check_str_name(cls, value, info):
|
66
|
+
Valida se os campos 'bucket_name' e 'filename' são strings não vazias.
|
67
|
+
Lança ValueError se a validação falhar.
|
68
|
+
check_destination(cls, value, info):
|
69
|
+
Valida se o campo 'destination' é uma string.
|
70
|
+
Lança ValueError se a validação falhar.
|
71
|
+
check_chunksize(cls, value, info):
|
72
|
+
Valida se o campo 'chunksize' é um inteiro.
|
73
|
+
Lança ValueError se a validação falhar.
|
74
|
+
"""
|
75
|
+
|
76
|
+
bucket_name:str
|
77
|
+
filename:str
|
78
|
+
destination:str
|
79
|
+
chunksize:int
|
80
|
+
|
81
|
+
@field_validator("bucket_name","filename")
|
82
|
+
def check_str_name(cls, value, info):
|
83
|
+
if not isinstance(value, str) or not value.strip():
|
84
|
+
raise ValueError(f"O parametro '{info.field_name}' deve ser uma string e não um {type(value)} e não vazio")
|
85
|
+
return value
|
86
|
+
|
87
|
+
@field_validator("destination")
|
88
|
+
def check_destination(cls, value, info):
|
89
|
+
if not isinstance(value, str) or value is not None:
|
90
|
+
raise ValueError(f"O parametro '{info.field_name}' deve ser uma string e não um {type(value)}")
|
91
|
+
return value
|
92
|
+
|
93
|
+
@field_validator("chunksize")
|
94
|
+
def check_destination(cls, value, info):
|
95
|
+
if not isinstance(value, int):
|
96
|
+
raise ValueError(f"O parametro '{info.field_name}' deve ser um inteiro e não um {type(value)}")
|
97
|
+
return value
|
98
|
+
|
99
|
+
class UploadFilesValidator(BaseModel):
|
100
|
+
"""
|
101
|
+
Classe UploadFilesValidator
|
102
|
+
Valida os parâmetros necessários para o upload de arquivos em um bucket GCP.
|
103
|
+
Atributos:
|
104
|
+
bucket_name (str): Nome do bucket onde o arquivo será armazenado.
|
105
|
+
filename (str): Nome do arquivo a ser enviado.
|
106
|
+
destination (str): Caminho de destino dentro do bucket.
|
107
|
+
Métodos:
|
108
|
+
check_str_name(cls, value, info):
|
109
|
+
Valida se os campos 'bucket_name' e 'filename' são strings não vazias.
|
110
|
+
Levanta um ValueError caso a validação falhe.
|
111
|
+
check_destination(cls, value, info):
|
112
|
+
Valida se o campo 'destination' é uma string.
|
113
|
+
Levanta um ValueError caso a validação falhe.
|
114
|
+
"""
|
115
|
+
|
116
|
+
bucket_name:str
|
117
|
+
filename:str
|
118
|
+
destination:str
|
119
|
+
|
120
|
+
@field_validator("bucket_name","filename")
|
121
|
+
def check_str_name(cls, value, info):
|
122
|
+
if not isinstance(value, str) or not value.strip():
|
123
|
+
raise ValueError(f"O parametro '{info.field_name}' deve ser uma string e não um {type(value)} e não vazio")
|
124
|
+
return value
|
125
|
+
|
126
|
+
@field_validator("destination")
|
127
|
+
def check_destination(cls, value, info):
|
128
|
+
if not isinstance(value, str) or value is not None:
|
129
|
+
raise ValueError(f"O parametro '{info.field_name}' deve ser uma string e não um {type(value)}")
|
130
|
+
return value
|
131
|
+
|
132
|
+
class DeleteFilesValidator(BaseModel):
|
133
|
+
"""
|
134
|
+
Classe DeleteFilesValidator
|
135
|
+
Valida os parâmetros necessários para a exclusão de arquivos em um bucket GCP.
|
136
|
+
Atributos:
|
137
|
+
bucket_name (str): Nome do bucket onde os arquivos estão armazenados.
|
138
|
+
filename (str): Nome do arquivo a ser excluído.
|
139
|
+
Métodos:
|
140
|
+
check_str_name(cls, value, info):
|
141
|
+
Valida se os valores fornecidos para os campos 'bucket_name' e 'filename'
|
142
|
+
são strings não vazias. Levanta um ValueError caso a validação falhe.
|
143
|
+
"""
|
144
|
+
|
145
|
+
bucket_name:str
|
146
|
+
filename:str
|
147
|
+
|
148
|
+
@field_validator("bucket_name","filename")
|
149
|
+
def check_str_name(cls, value, info):
|
150
|
+
if not isinstance(value, str) or not value.strip():
|
151
|
+
raise ValueError(f"O parametro '{info.field_name}' deve ser uma string e não um {type(value)} e não vazio")
|
152
|
+
return value
|