diff --git a/docs/Makefile b/docs/Makefile index dbb9519..2ae0797 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -11,7 +11,49 @@ BUILDDIR = build help: @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) -.PHONY: help Makefile +# Clean build directory +clean: + rm -rf $(BUILDDIR)/* + +# Build HTML documentation +html: + $(SPHINXBUILD) -b html $(SPHINXOPTS) $(SOURCEDIR) $(BUILDDIR)/html + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." + +# Build HTML and open in browser +serve: html + @echo "Opening documentation in browser..." + @if command -v open >/dev/null 2>&1; then \ + open $(BUILDDIR)/html/index.html; \ + elif command -v xdg-open >/dev/null 2>&1; then \ + xdg-open $(BUILDDIR)/html/index.html; \ + else \ + echo "Please open $(BUILDDIR)/html/index.html in your browser"; \ + fi + +# Build PDF documentation +pdf: + $(SPHINXBUILD) -b latex $(SPHINXOPTS) $(SOURCEDIR) $(BUILDDIR)/latex + @echo "Running LaTeX files through pdflatex..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +# Check for broken links +linkcheck: + $(SPHINXBUILD) -b linkcheck $(SPHINXOPTS) $(SOURCEDIR) $(BUILDDIR)/linkcheck + @echo + @echo "Link check finished; look for any errors in the above output " \ + "or in $(BUILDDIR)/linkcheck/output.txt." + +# Spell check (requires sphinxcontrib-spelling) +spelling: + $(SPHINXBUILD) -b spelling $(SPHINXOPTS) $(SOURCEDIR) $(BUILDDIR)/spelling + @echo + @echo "Spell check finished; look for any errors in the above output " \ + "or in $(BUILDDIR)/spelling/output.txt." + +.PHONY: help Makefile clean html serve pdf linkcheck spelling # Catch-all target: route all unknown targets to Sphinx using the new # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). diff --git a/docs/build/html/.buildinfo b/docs/build/html/.buildinfo index 2994bb0..11e5427 100644 --- a/docs/build/html/.buildinfo +++ b/docs/build/html/.buildinfo @@ -1,4 +1,4 @@ # Sphinx build info version 1 # This file records the configuration used when building these files. When it is not found, a full rebuild will be done. -config: 06e842708d34c14d7ed3fe2bd8dd5165 +config: 5f692cdea773b403afcc7068483ecb67 tags: 645f666f9bcd5a90fca523b33c5a78b7 diff --git a/docs/build/html/.doctrees/advanced_usage.doctree b/docs/build/html/.doctrees/advanced_usage.doctree new file mode 100644 index 0000000..d0ea96a Binary files /dev/null and b/docs/build/html/.doctrees/advanced_usage.doctree differ diff --git a/docs/build/html/.doctrees/api.doctree b/docs/build/html/.doctrees/api.doctree new file mode 100644 index 0000000..d6c0590 Binary files /dev/null and b/docs/build/html/.doctrees/api.doctree differ diff --git a/docs/build/html/.doctrees/configuration.doctree b/docs/build/html/.doctrees/configuration.doctree new file mode 100644 index 0000000..7bd42dc Binary files /dev/null and b/docs/build/html/.doctrees/configuration.doctree differ diff --git a/docs/build/html/.doctrees/environment.pickle b/docs/build/html/.doctrees/environment.pickle new file mode 100644 index 0000000..33b1262 Binary files /dev/null and b/docs/build/html/.doctrees/environment.pickle differ diff --git a/docs/build/html/.doctrees/examples.doctree b/docs/build/html/.doctrees/examples.doctree new file mode 100644 index 0000000..c74edaa Binary files /dev/null and b/docs/build/html/.doctrees/examples.doctree differ diff --git a/docs/build/html/.doctrees/formats.doctree b/docs/build/html/.doctrees/formats.doctree new file mode 100644 index 0000000..54c5ad1 Binary files /dev/null and b/docs/build/html/.doctrees/formats.doctree differ diff --git a/docs/build/html/.doctrees/index.doctree b/docs/build/html/.doctrees/index.doctree new file mode 100644 index 0000000..a9480cf Binary files /dev/null and b/docs/build/html/.doctrees/index.doctree differ diff --git a/docs/build/html/.doctrees/installation.doctree b/docs/build/html/.doctrees/installation.doctree new file mode 100644 index 0000000..052e4ba Binary files /dev/null and b/docs/build/html/.doctrees/installation.doctree differ diff --git a/docs/build/html/.doctrees/quickstart.doctree b/docs/build/html/.doctrees/quickstart.doctree new file mode 100644 index 0000000..dd611b1 Binary files /dev/null and b/docs/build/html/.doctrees/quickstart.doctree differ diff --git a/docs/build/html/.nojekyll b/docs/build/html/.nojekyll deleted file mode 100644 index e69de29..0000000 diff --git a/docs/build/html/_modules/atio/core.html b/docs/build/html/_modules/atio/core.html new file mode 100644 index 0000000..210fbdd --- /dev/null +++ b/docs/build/html/_modules/atio/core.html @@ -0,0 +1,600 @@ + + + + + + + + atio.core — Atio 2.0.0 문서 + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

atio.core의 소스 코드

+"""progress 적용 후 write 함수"""
+import os
+import tempfile
+import threading
+import time
+import numpy as np
+from queue import Queue
+from .plugins import get_writer
+from .utils import setup_logger, ProgressBar
+
+
+[문서] +def write(obj, target_path=None, format=None, show_progress=False, verbose=False, **kwargs): + """ + 데이터 객체(obj)를 안전하게 target_path 또는 데이터베이스에 저장합니다. + + - 파일 기반 쓰기 (format: 'csv', 'parquet', 'excel' 등): + - target_path (str): 필수. 데이터가 저장될 파일 경로입니다. + - 롤백 기능이 있는 원자적 쓰기를 수행합니다. + + - 데이터베이스 기반 쓰기 (format: 'sql', 'database'): + - target_path: 사용되지 않습니다. + - kwargs (dict): 데이터베이스 쓰기에 필요한 추가 인자들입니다. + - pandas.to_sql: 'name'(테이블명), 'con'(커넥션 객체)가 필수입니다. + - polars.write_database: 'table_name', 'connection_uri'가 필수입니다. + + Args: + obj: 저장할 데이터 객체 (e.g., pandas.DataFrame, polars.DataFrame, np.ndarray). + target_path (str, optional): 파일 저장 경로. 파일 기반 쓰기 시 필수. Defaults to None. + format (str, optional): 저장할 포맷. Defaults to None. + show_progress (bool): 진행도 표시 여부. Defaults to False. + verbose (bool): 상세한 성능 진단 정보 출력 여부. Defaults to False. + **kwargs: 각 쓰기 함수에 전달될 추가 키워드 인자. + """ + logger = setup_logger(debug_level=verbose) + t0 = time.perf_counter() + + # --- 1. 데이터베이스 쓰기 특별 처리 --- + # 데이터베이스 쓰기는 파일 경로 기반의 원자적 쓰기 로직을 따르지 않습니다. + if format in ('sql', 'database'): + logger.info(f"데이터베이스 쓰기 모드 시작 (format: {format})") + writer_method_name = get_writer(obj, format) + + if writer_method_name is None: + err_msg = f"객체 타입 {type(obj).__name__}에 대해 지원하지 않는 format: {format}" + logger.error(err_msg) + raise ValueError(err_msg) + + try: + writer_func = getattr(obj, writer_method_name) + + # 각 데이터베이스 쓰기 함수에 필요한 필수 인자 확인 + if format == 'sql': # Pandas + if 'name' not in kwargs or 'con' not in kwargs: + raise ValueError("'name'(테이블명)과 'con'(DB 커넥션) 인자는 'sql' 포맷에 필수입니다.") + elif format == 'database': # Polars + if 'table_name' not in kwargs or 'connection_uri' not in kwargs: + raise ValueError("'table_name'과 'connection_uri' 인자는 'database' 포맷에 필수입니다.") + + # target_path는 무시하고 **kwargs로 받은 인자들을 사용하여 DB에 직접 씁니다. + writer_func(**kwargs) + + t_end = time.perf_counter() + logger.info(f"✅ 데이터베이스 쓰기 완료 (총 소요 시간: {t_end - t0:.4f}s)") + return # DB 쓰기 완료 후 함수 종료 + + except Exception as e: + t_err = time.perf_counter() + logger.error(f"데이터베이스 쓰기 중 예외 발생: {e}") + logger.info(f"데이터베이스 쓰기 실패 (소요 시간: {t_err - t0:.4f}s, 에러: {type(e).__name__})") + raise e + + # --- 2. 파일 기반 원자적 쓰기 --- + if target_path is None: + raise ValueError("파일 기반 쓰기(예: 'csv', 'parquet')에는 'target_path' 인자가 필수입니다.") + + dir_name = os.path.dirname(os.path.abspath(target_path)) + base_name = os.path.basename(target_path) + os.makedirs(dir_name, exist_ok=True) + + # 롤백을 위한 백업 경로 설정 + backup_path = target_path + "._backup" + original_exists = os.path.exists(target_path) + + with tempfile.TemporaryDirectory(dir=dir_name) as tmpdir: + tmp_path = os.path.join(tmpdir, base_name) + logger.info(f"임시 디렉토리 생성: {tmpdir}") + logger.info(f"임시 파일 경로: {tmp_path}") + + t1 = time.perf_counter() + + writer = get_writer(obj, format) + if writer is None: + logger.error(f"지원하지 않는 format: {format}") + if verbose: + logger.debug(f"Atomic write step timings (FAILED at setup): " + f"setup={t1-t0:.4f}s, total={time.perf_counter()-t0:.4f}s") + logger.info(f"Atomic write failed at setup stage (took {time.perf_counter()-t0:.4f}s)") + raise ValueError(f"지원하지 않는 format: {format}") + logger.info(f"사용할 writer: {writer} (format: {format})") + + try: + if not show_progress: + _execute_write(writer, obj, tmp_path, **kwargs) + else: + _execute_write_with_progress(writer, obj, tmp_path, **kwargs) + + t2 = time.perf_counter() + logger.info(f"데이터 임시 파일에 저장 완료: {tmp_path}") + + except Exception as e: + # 쓰기 실패 시에는 롤백할 필요가 없음 (원본 파일은 그대로 있음) + t_error = time.perf_counter() + logger.error(f"임시 파일 저장 중 예외 발생: {e}") + if verbose: + logger.debug(f"Atomic write step timings (ERROR during write): " + f"setup={t1-t0:.4f}s, write_call={t_error-t1:.4f}s (실패), " + f"total={t_error-t0:.4f}s, error_type={type(e).__name__}") + logger.info(f"Atomic write failed during write stage (took {t_error-t0:.4f}s, error: {type(e).__name__})") + raise e + + # [롤백 STEP 1] 기존 파일 백업 + if original_exists: + logger.info(f"기존 파일 백업: {target_path} -> {backup_path}") + try: + # rename은 atomic 연산이므로 백업 과정도 안전합니다. + os.rename(target_path, backup_path) + except Exception as e: + logger.error(f"백업 생성 실패. 작업을 중단합니다: {e}") + # 백업 실패 시 더 이상 진행하면 안 되므로 예외를 발생시킵니다. + raise IOError(f"Failed to create backup for {target_path}") from e + + try: + # [롤백 STEP 2] 원자적 교체 + os.replace(tmp_path, target_path) + t3 = time.perf_counter() + logger.info(f"원자적 교체 완료: {tmp_path} -> {target_path}") + + # [롤백 STEP 3] _SUCCESS 플래그 생성 + success_path = os.path.join(os.path.dirname(target_path), f".{os.path.basename(target_path)}._SUCCESS") + with open(success_path, "w") as f: + f.write("OK\n") + t4 = time.perf_counter() + logger.info(f"_SUCCESS 플래그 파일 생성: {success_path}") + + # [롤백 STEP 4] 성공 시 백업 파일 삭제 + if original_exists: + os.remove(backup_path) + logger.info(f"작업 성공, 백업 파일 삭제 완료: {backup_path}") + + if verbose: + logger.debug(f"Atomic write step timings (SUCCESS): " + f"setup={t1-t0:.4f}s, write_call={t2-t1:.4f}s, " + f"replace={t3-t2:.4f}s, success_flag={t4-t3:.4f}s, " + f"total={t4-t0:.4f}s") + logger.info(f"✅ Atomic write completed successfully (took {t4-t0:.4f}s)") + + except Exception as e: + # [롤백 STEP 5] 교체 또는 플래그 생성 실패 시 롤백 실행 + t_final_error = time.perf_counter() + logger.error(f"최종 저장 단계에서 오류 발생. 롤백을 시작합니다. 원인: {e}") + + if original_exists: + try: + # 새로 쓴 불완전한 파일이 있다면 삭제 + if os.path.exists(target_path): + os.remove(target_path) + + # 백업해둔 원본 파일을 다시 복구 + os.rename(backup_path, target_path) + logger.info(f"롤백 성공: 원본 파일 복구 완료 ({backup_path} -> {target_path})") + + except Exception as rollback_e: + logger.critical(f"치명적 오류: 롤백 실패! {rollback_e}") + logger.critical(f"시스템이 불안정한 상태일 수 있습니다. 수동 확인이 필요합니다.") + logger.critical(f"남아있는 파일: (새 데이터) {target_path}, (원본 백업) {backup_path}") + + if verbose: + logger.debug(f"Atomic write step timings (FAILED AND ROLLED BACK): " + f"setup={t1-t0:.4f}s, write_call={t2-t1:.4f}s, " + f"final_stage_fail_time={t_final_error-t2:.4f}s, " + f"total={t_final_error-t0:.4f}s, error_type={type(e).__name__}") + logger.info(f"Atomic write failed and rolled back (took {t_final_error-t0:.4f}s, error: {type(e).__name__})") + + # 원본 예외를 다시 발생시켜 사용자에게 알립니다. + raise e
+ + +def _execute_write(writer, obj, path, **kwargs): + """ + 내부 쓰기 실행 함수. 핸들러 타입에 따라 분기하여 실제 쓰기 작업을 수행합니다. + - callable(writer): `np.save`와 같은 함수 핸들러 + - str(writer): `to_csv`와 같은 객체의 메소드 핸들러 + """ + # 1. writer가 호출 가능한 '함수'인 경우 (e.g., np.save, np.savetxt) + if callable(writer): + # 1a. np.savez, np.savez_compressed 특별 처리: 여러 배열을 dict로 받아 저장 + if writer in (np.savez, np.savez_compressed): + if not isinstance(obj, dict): + raise TypeError( + f"'{writer.__name__}'로 여러 배열을 저장하려면, " + f"데이터 객체는 dict 타입이어야 합니다. (현재: {type(obj).__name__})" + ) + writer(path, **obj) + # 1b. 그 외 일반적인 함수 핸들러 처리 + else: + writer(path, obj, **kwargs) + + # 2. writer가 '메소드 이름(문자열)'인 경우 (e.g., 'to_csv', 'to_excel') + # 이 경우, obj.to_csv(path, **kwargs) 와 같이 호출됩니다. + else: + getattr(obj, writer)(path, **kwargs) + +def _execute_write_with_progress(writer, obj, path, **kwargs): + """멀티스레딩으로 쓰기 작업과 진행도 표시를 함께 실행하는 내부 함수""" + stop_event = threading.Event() + exception_queue = Queue() + + # 실제 쓰기 작업을 수행할 '작업 스레드'의 목표 함수 + def worker_task(): + try: + _execute_write(writer, obj, path, **kwargs) + except Exception as e: + exception_queue.put(e) + + # 스레드 생성 + worker_thread = threading.Thread(target=worker_task) + progress_bar = ProgressBar(filepath=path, stop_event=stop_event, description="Writing") + monitor_thread = threading.Thread(target=progress_bar.run) + + # 스레드 시작 + worker_thread.start() + monitor_thread.start() + + # 작업 스레드가 끝날 때까지 대기 + worker_thread.join() + + # 모니터 스레드에 중지 신호 전송 및 종료 대기 + stop_event.set() + monitor_thread.join() + + # 작업 스레드에서 예외가 발생했는지 확인하고, 있었다면 다시 발생시킴 + if not exception_queue.empty(): + raise exception_queue.get_nowait() + +import uuid +from .utils import read_json, write_json + +
+[문서] +def write_snapshot(obj, table_path, mode='overwrite', format='parquet', **kwargs): + logger = setup_logger(debug_level=False) + + # 1. 경로 설정 및 폴더 생성 + os.makedirs(os.path.join(table_path, 'data'), exist_ok=True) + os.makedirs(os.path.join(table_path, 'metadata'), exist_ok=True) + + # 2. 현재 버전 확인 + pointer_path = os.path.join(table_path, '_current_version.json') + current_version = 0 + if os.path.exists(pointer_path): + current_version = read_json(pointer_path)['version_id'] + new_version = current_version + 1 + + # 3. 임시 디렉토리 내에서 모든 작업 수행 + with tempfile.TemporaryDirectory() as tmpdir: + # 3a. 새 데이터 파일 쓰기 + writer = get_writer(obj, format) + data_filename = f"{uuid.uuid4()}.{format}" + tmp_data_path = os.path.join(tmpdir, data_filename) + _execute_write(writer, obj, tmp_data_path, **kwargs) + + # 3. 임시 디렉토리 내에서 모든 작업 수행 + with tempfile.TemporaryDirectory() as tmpdir: + # 3a. 새 데이터 파일 쓰기 + writer = get_writer(obj, format) + if writer is None: + raise ValueError(f"지원하지 않는 format: {format} for object type {type(obj)}") + + data_filename = f"{uuid.uuid4()}.{format}" + tmp_data_path = os.path.join(tmpdir, data_filename) + _execute_write(writer, obj, tmp_data_path, **kwargs) + + # 3b. 새 manifest 생성 + new_manifest = { + 'files': [{'path': os.path.join('data', data_filename), 'format': format}] + } + manifest_filename = f"manifest-{uuid.uuid4()}.json" + write_json(new_manifest, os.path.join(tmpdir, manifest_filename)) + + # 3c. 새 snapshot 생성을 위한 준비 + all_manifests = [os.path.join('metadata', manifest_filename)] + + if mode.lower() == 'append' and current_version > 0: + try: + prev_metadata_path = os.path.join(table_path, 'metadata', f'v{current_version}.metadata.json') + prev_metadata = read_json(prev_metadata_path) + prev_snapshot_filename = prev_metadata['snapshot_filename'] + + prev_snapshot_path = os.path.join(table_path, prev_snapshot_filename) + prev_snapshot = read_json(prev_snapshot_path) + existing_manifests = prev_snapshot['manifests'] + + all_manifests.extend(existing_manifests) + except (FileNotFoundError, KeyError): + logger.warning(f"Append mode: 이전 버전(v{current_version})의 메타데이터를 찾을 수 없거나 형식이 올바르지 않습니다. Overwrite 모드로 동작합니다.") + + # 3d. 최종 manifest 목록으로 새 snapshot 생성 + snapshot_id = int(time.time()) + snapshot_filename = f"snapshot-{snapshot_id}-{uuid.uuid4()}.json" + + new_snapshot = { + 'snapshot_id': snapshot_id, + 'timestamp': time.time(), + 'manifests': all_manifests + } + write_json(new_snapshot, os.path.join(tmpdir, snapshot_filename)) + + # 3e. 새 version metadata 생성 + new_metadata = { + 'version_id': new_version, + 'snapshot_id': snapshot_id, + 'snapshot_filename': os.path.join('metadata', snapshot_filename) + } + metadata_filename = f"v{new_version}.metadata.json" + write_json(new_metadata, os.path.join(tmpdir, metadata_filename)) + + # 3f. 새 포인터 파일 생성 + new_pointer = {'version_id': new_version} + tmp_pointer_path = os.path.join(tmpdir, '_current_version.json') + write_json(new_pointer, tmp_pointer_path) + + # 4. 최종 커밋 + os.rename(tmp_data_path, os.path.join(table_path, 'data', data_filename)) + os.rename(os.path.join(tmpdir, manifest_filename), os.path.join(table_path, 'metadata', manifest_filename)) + os.rename(os.path.join(tmpdir, snapshot_filename), os.path.join(table_path, 'metadata', snapshot_filename)) + os.rename(os.path.join(tmpdir, metadata_filename), os.path.join(table_path, 'metadata', metadata_filename)) + os.replace(tmp_pointer_path, pointer_path) + logger.info(f"스냅샷 쓰기 완료! '{table_path}'가 버전 {new_version}으로 업데이트되었습니다.")
+ + + + +
+[문서] +def read_table(table_path, version=None, output_as='pandas'): + # 1. 읽을 버전 결정 및 진입점(metadata.json) 찾기 + pointer_path = os.path.join(table_path, '_current_version.json') + if version is None: + version_id = read_json(pointer_path)['version_id'] + else: + version_id = version + + metadata_path = os.path.join(table_path, 'metadata', f'v{version_id}.metadata.json') + metadata = read_json(metadata_path) + snapshot_filepath = metadata['snapshot_filename'] + + # 2. metadata -> snapshot -> manifest 순으로 파싱 + snapshot_path = os.path.join(table_path, snapshot_filepath) # 정확한 경로 사용 + snapshot = read_json(snapshot_path) + + # 3. 모든 manifest를 읽어 최종 데이터 파일 목록 취합 + all_data_files = [] + for manifest_ref in snapshot['manifests']: + manifest_path = os.path.join(table_path, manifest_ref) + manifest = read_json(manifest_path) + for file_info in manifest['files']: + # file_info 에는 path, format 등의 정보가 있음 + all_data_files.append(os.path.join(table_path, file_info['path'])) + + # 4. output_as 옵션에 따라 최종 데이터 객체 생성 + if not all_data_files: + # 데이터가 없는 경우 처리 + return None # 또는 빈 DataFrame + + if output_as == 'pandas': + import pandas as pd + return pd.read_parquet(all_data_files) + elif output_as == 'polars': + import polars as pl + return pl.read_parquet(all_data_files) + # NumPy 등의 다른 형식 처리 로직 추가 + + raise ValueError(f"지원하지 않는 출력 형식: {output_as}")
+ + + +from datetime import datetime, timedelta + +
+[문서] +def expire_snapshots(table_path, keep_for=timedelta(days=7), dry_run=True): + """ + 설정된 보관 기간(keep_for)보다 오래된 스냅샷과 + 더 이상 참조되지 않는 데이터 파일을 삭제합니다. + """ + logger = setup_logger() + now = datetime.now() + metadata_dir = os.path.join(table_path, 'metadata') + + if not os.path.isdir(metadata_dir): + logger.info("정리할 테이블이 없거나 메타데이터 폴더를 찾을 수 없습니다.") + return + + # --- 1. 모든 메타데이터 정보와 파일명 수집 --- + all_versions_meta = {} # version_id -> version_meta + all_snapshots_meta = {} # snapshot_id -> snapshot_meta + all_manifest_paths = set() # 모든 manifest 파일 경로 + + for filename in os.listdir(metadata_dir): + path = os.path.join(metadata_dir, filename) + if filename.startswith('v') and filename.endswith('.metadata.json'): + meta = read_json(path) + all_versions_meta[meta['version_id']] = meta + elif filename.startswith('snapshot-'): + snap = read_json(path) + all_snapshots_meta[snap['snapshot_id']] = snap + elif filename.startswith('manifest-'): + all_manifest_paths.add(os.path.join('metadata', filename)) + + # --- 2. "살아있는" 객체 식별 --- + live_snapshot_ids = set() + live_manifests = set() + live_data_files = set() + + # 현재 버전을 포함하여 보관 기간 내의 모든 버전을 "살아있는" 것으로 간주 + for version_meta in all_versions_meta.values(): + snapshot_id = version_meta['snapshot_id'] + snapshot = all_snapshots_meta.get(snapshot_id) + + if snapshot and (now - datetime.fromtimestamp(snapshot['timestamp'])) < keep_for: + live_snapshot_ids.add(snapshot_id) + for manifest_ref in snapshot.get('manifests', []): + live_manifests.add(manifest_ref) + manifest_path = os.path.join(table_path, manifest_ref) + if os.path.exists(manifest_path): + manifest_data = read_json(manifest_path) + for file_info in manifest_data.get('files', []): + live_data_files.add(file_info['path']) + + # --- 3. 삭제할 "고아" 객체 식별 --- + files_to_delete = [] + + # 고아 데이터 파일 찾기 + data_dir = os.path.join(table_path, 'data') + if os.path.isdir(data_dir): + for data_file in os.listdir(data_dir): + relative_path = os.path.join('data', data_file) + if relative_path not in live_data_files: + files_to_delete.append(os.path.join(table_path, relative_path)) + + # 고아 매니페스트 파일 찾기 + manifests_to_delete = all_manifest_paths - live_manifests + for manifest_path in manifests_to_delete: + files_to_delete.append(os.path.join(table_path, manifest_path)) + + # 고아 스냅샷 및 버전 메타데이터 파일 찾기 + for version_id, version_meta in all_versions_meta.items(): + snapshot_id = version_meta['snapshot_id'] + if snapshot_id not in live_snapshot_ids: + # vX.metadata.json 파일 삭제 대상 추가 + files_to_delete.append(os.path.join(metadata_dir, f"v{version_id}.metadata.json")) + # snapshot-X.json 파일 삭제 대상 추가 + snapshot_filename = version_meta.get('snapshot_filename') # 이전 단계에서 이 키를 추가했었음 + if snapshot_filename: + files_to_delete.append(os.path.join(table_path, snapshot_filename)) + + # 중복 제거 + files_to_delete = sorted(list(set(files_to_delete))) + + # --- 4. 최종 삭제 실행 --- + if not files_to_delete: + logger.info("삭제할 오래된 파일이 없습니다.") + return + + logger.info(f"총 {len(files_to_delete)}개의 오래된 파일을 찾았습니다.") + if dry_run: + logger.info("[Dry Run] 아래 파일들이 삭제될 예정입니다:") + for f in files_to_delete: + print(f" - {f}") + else: + logger.info("오래된 파일들을 삭제합니다...") + for f in files_to_delete: + try: + os.remove(f) + logger.debug(f" - 삭제됨: {f}") + except OSError as e: + logger.error(f" - 삭제 실패: {f}, 오류: {e}") + logger.info("삭제 작업이 완료되었습니다.")
+ +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/build/html/_modules/atio/plugins.html b/docs/build/html/_modules/atio/plugins.html new file mode 100644 index 0000000..c45d8a7 --- /dev/null +++ b/docs/build/html/_modules/atio/plugins.html @@ -0,0 +1,229 @@ + + + + + + + + atio.plugins — Atio 2.0.0 문서 + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

atio.plugins의 소스 코드

+from .utils import setup_logger
+# 추후 Pandas, Polars, Numpy 등 확장 지원을 위한 플러그인 구조
+
+logger = setup_logger()
+
+# { 객체 타입: { 포맷: 핸들러 } }
+# 핸들러는 '메소드 이름(str)' 또는 '호출 가능한 함수'가 될 수 있습니다.
+WRITER_MAPPING = {}
+
+
+[문서] +def register_writer(obj_type, fmt, handler): + """(객체 타입, 포맷) 쌍으로 쓰기 핸들러를 등록""" + if obj_type not in WRITER_MAPPING: + WRITER_MAPPING[obj_type] = {} + WRITER_MAPPING[obj_type][fmt] = handler + logger.debug(f"Writer registered: type={obj_type.__name__}, format={fmt}, handler={handler}")
+ + +
+[문서] +def get_writer(obj, fmt): + """객체의 타입과 포맷에 맞는 핸들러를 조회""" + obj_type = type(obj) + handler = WRITER_MAPPING.get(obj_type, {}).get(fmt) + if handler is None: + logger.warning(f"No writer found for type {obj_type.__name__} and format '{fmt}'") + return handler
+ + +# --------------------------------------------------------------------------- +# 1. Pandas 쓰기 방법 등록 +# --------------------------------------------------------------------------- +try: + import pandas as pd + + PANDAS_DF_TYPE = pd.DataFrame + + # Pandas DataFrame에 대한 쓰기 핸들러 등록 + # 값은 DataFrame 객체의 메소드 이름(문자열)입니다. + # 예: format='csv' -> df.to_csv(...) 호출 + register_writer(PANDAS_DF_TYPE, "csv", "to_csv") + register_writer(PANDAS_DF_TYPE, "parquet", "to_parquet") + register_writer(PANDAS_DF_TYPE, "json", "to_json") + register_writer(PANDAS_DF_TYPE, "pickle", "to_pickle") + register_writer(PANDAS_DF_TYPE, "html", "to_html") + + # Excel 쓰기. `openpyxl` 라이브러리가 필요합니다. + # pip install openpyxl + register_writer(PANDAS_DF_TYPE, "excel", "to_excel") + + # SQL 쓰기. `sqlalchemy` 라이브러리가 필요합니다. + # 이 핸들러는 core.py에서 특별 처리됩니다 (파일 시스템을 사용하지 않음). + # pip install sqlalchemy + register_writer(PANDAS_DF_TYPE, "sql", "to_sql") + + logger.info("Pandas writers registered successfully.") + +except ImportError: + logger.info("Pandas not found. Skipping pandas writer registration.") + pass + +# --------------------------------------------------------------------------- +# 2. Polars 쓰기 방법 등록 +# --------------------------------------------------------------------------- +try: + import polars as pl + + POLARS_DF_TYPE = pl.DataFrame + + # Polars DataFrame에 대한 쓰기 핸들러 등록 + register_writer(POLARS_DF_TYPE, "csv", "write_csv") + register_writer(POLARS_DF_TYPE, "parquet", "write_parquet") + register_writer(POLARS_DF_TYPE, "json", "write_json") + register_writer(POLARS_DF_TYPE, "ipc", "write_ipc") + register_writer(POLARS_DF_TYPE, "avro", "write_avro") + + # Polars Excel 쓰기. `xlsx2csv`와 `openpyxl`이 필요할 수 있습니다. + # pip install xlsx2csv openpyxl + register_writer(POLARS_DF_TYPE, "excel", "write_excel") + + # Polars 데이터베이스 쓰기. connector-x 가 필요합니다. + # pip install connectorx + # 이 핸들러는 core.py에서 특별 처리됩니다. + register_writer(POLARS_DF_TYPE, "database", "write_database") + + logger.info("Polars writers registered successfully.") + +except ImportError: + logger.info("Polars not found. Skipping polars writer registration.") + pass + +# --------------------------------------------------------------------------- +# 3. NumPy 쓰기 방법 등록 +# --------------------------------------------------------------------------- +try: + import numpy as np + + NUMPY_NDARRAY_TYPE = np.ndarray + + # NumPy는 저장 방식이 메소드와 함수가 섞여있어 구분이 중요합니다. + # 값: 실제 '함수 객체' (호출 방식: np.save(path, arr)) + register_writer(NUMPY_NDARRAY_TYPE, "npy", np.save) + register_writer(NUMPY_NDARRAY_TYPE, "npz", np.savez) + register_writer(NUMPY_NDARRAY_TYPE, "npz_compressed", np.savez_compressed) + register_writer(NUMPY_NDARRAY_TYPE, "csv", np.savetxt) + + # 값: '메소드 이름(문자열)' (호출 방식: arr.tofile(path)) + register_writer(NUMPY_NDARRAY_TYPE, "bin", "tofile") + + # 여러 배열을 한 번에 저장하기 위해 dict 타입도 지원 + register_writer(dict, "npz", np.savez) + register_writer(dict, "npz_compressed", np.savez_compressed) + + logger.info("NumPy writers registered successfully.") + +except ImportError: + logger.info("NumPy not found. Skipping numpy writer registration.") + pass +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/build/html/_modules/atio/utils.html b/docs/build/html/_modules/atio/utils.html new file mode 100644 index 0000000..cc30da0 --- /dev/null +++ b/docs/build/html/_modules/atio/utils.html @@ -0,0 +1,248 @@ + + + + + + + + atio.utils — Atio 2.0.0 문서 + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

atio.utils의 소스 코드

+import logging
+import os
+
+
+
+[문서] +def setup_logger(name="atio", debug_level=False): + logger = logging.getLogger(name) + if not logger.handlers: + handler = logging.StreamHandler() + formatter = logging.Formatter("[%(levelname)s] %(message)s") + handler.setFormatter(formatter) + logger.addHandler(handler) + + # debug_level이 True이면 DEBUG 레벨로 설정, 아니면 INFO 레벨 + if debug_level: + logger.setLevel(logging.DEBUG) + else: + logger.setLevel(logging.INFO) + + return logger
+ + + +
+[문서] +def check_file_exists(path): + return os.path.exists(path)
+ + + +import time +import threading + + +
+[문서] +class ProgressBar: + """ + 파일 쓰기 진행 상황을 콘솔에 표시하는 클래스. + 스피너, 처리된 용량, 처리 속도, 경과 시간을 표시합니다. + """ + +
+[문서] + def __init__(self, filepath: str, stop_event: threading.Event, description: str = "Writing"): + self.filepath = filepath + self.stop_event = stop_event + self.description = description + + self.spinner_chars = "⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏" + self.start_time = time.time()
+ + + def _format_size(self, size_bytes: int) -> str: + """바이트를 KB, MB, GB 등 읽기 좋은 형태로 변환합니다.""" + if size_bytes < 1024: + return f"{size_bytes} B" + elif size_bytes < 1024**2: + return f"{size_bytes/1024:.1f} KB" + elif size_bytes < 1024**3: + return f"{size_bytes/1024**2:.1f} MB" + else: + return f"{size_bytes/1024**3:.1f} GB" + +
+[문서] + def run(self): + """ + 진행도 막대를 실행하는 메인 루프. + 이 함수가 모니터링 스레드에서 실행됩니다. + """ + spinner_index = 0 + while not self.stop_event.is_set(): + spinner_char = self.spinner_chars[spinner_index % len(self.spinner_chars)] + + try: + current_size = os.path.getsize(self.filepath) + except FileNotFoundError: + current_size = 0 + + elapsed_time = time.time() - self.start_time + + # 0으로 나누기 방지 + speed = current_size / elapsed_time if elapsed_time > 0 else 0 + + # 시간 포맷팅 (MM:SS) + mins, secs = divmod(int(elapsed_time), 60) + time_str = f"{mins:02d}:{secs:02d}" + + # 최종 출력 문자열 생성 + progress_line = ( + f"\r{spinner_char} {self.description} {os.path.basename(self.filepath)}... " + f"[ {self._format_size(current_size)} | {self._format_size(speed)}/s | {time_str} ]" + ) + + # 콘솔에 한 줄 출력 (덮어쓰기) + print(progress_line, end="", flush=True) + + spinner_index += 1 + time.sleep(0.1) # 0.1초마다 업데이트하여 CPU 사용 최소화 + + # 루프가 끝나면 마지막 완료 메시지를 출력 + self._finish()
+ + + def _finish(self): + """작업 완료 후 깔끔한 최종 메시지를 출력합니다.""" + final_size = os.path.getsize(self.filepath) + elapsed_time = time.time() - self.start_time + time_str = f"{int(elapsed_time)}s" + + # 기존 줄을 지우기 위해 공백으로 덮어씁니다. + clear_line = "\r" + " " * 80 + "\r" + + finish_message = ( + f"✔︎ Finished {self.description} {os.path.basename(self.filepath)}. " + f"({self._format_size(final_size)} in {time_str})" + ) + print(clear_line + finish_message, flush=True)
+ + + +import json + +
+[문서] +def read_json(path: str): + with open(path, 'r', encoding='utf-8') as f: + return json.load(f)
+ + +
+[문서] +def write_json(data: dict, path: str): + with open(path, 'w', encoding='utf-8') as f: + json.dump(data, f, indent=4)
+ +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/build/html/api/modules.html b/docs/build/html/_modules/index.html similarity index 56% rename from docs/build/html/api/modules.html rename to docs/build/html/_modules/index.html index d74f47c..4ce3508 100644 --- a/docs/build/html/api/modules.html +++ b/docs/build/html/_modules/index.html @@ -1,26 +1,24 @@ - + - - + - Modules — atio documentation + 개요: 모듈 코드 — Atio 2.0.0 문서 - + + - - - - + + @@ -32,7 +30,7 @@ - atio + Atio
@@ -42,12 +40,17 @@
@@ -56,7 +59,7 @@
@@ -64,9 +67,8 @@

@@ -74,29 +76,20 @@
-
-

Modules

- - - - - - -

atio

Atio: 안전한 원자적 파일 쓰기 라이브러리

-
- +

코드를 확인할 수 있는 모든 모듈

+
-
+

-

© Copyright 2025, jujangchoi.

+

© Copyright 2025, Seo Jae Oh.

Built with Sphinx using a diff --git a/docs/build/html/_sources/advanced_usage.rst.txt b/docs/build/html/_sources/advanced_usage.rst.txt new file mode 100644 index 0000000..f8eafc9 --- /dev/null +++ b/docs/build/html/_sources/advanced_usage.rst.txt @@ -0,0 +1,323 @@ +고급 사용법 +========== + +이 섹션에서는 Atio의 고급 기능들을 다룹니다. + +스냅샷 기반 버전 관리 +-------------------- + +Atio는 데이터의 버전을 관리할 수 있는 스냅샷 시스템을 제공합니다. + +기본 스냅샷 쓰기 +~~~~~~~~~~~~~~~ + +.. code-block:: python + + import atio + import pandas as pd + + # 데이터 생성 + df = pd.DataFrame({ + "id": [1, 2, 3], + "name": ["Alice", "Bob", "Charlie"] + }) + + # 스냅샷으로 저장 (버전 관리) + atio.write_snapshot(df, "users_table", format="parquet") + + # 새로운 데이터로 업데이트 + df_new = pd.DataFrame({ + "id": [1, 2, 3, 4], + "name": ["Alice", "Bob", "Charlie", "David"] + }) + + # append 모드로 스냅샷 추가 + atio.write_snapshot(df_new, "users_table", mode="append", format="parquet") + +스냅샷 읽기 +~~~~~~~~~~ + +.. code-block:: python + + # 최신 버전 읽기 + latest_data = atio.read_table("users_table") + + # 특정 버전 읽기 + version_1_data = atio.read_table("users_table", version=1) + + # Polars로 읽기 + polars_data = atio.read_table("users_table", output_as="polars") + +스냅샷 정리 +~~~~~~~~~~ + +.. code-block:: python + + from datetime import timedelta + + # 7일 이상 된 스냅샷 삭제 (dry run) + atio.expire_snapshots("users_table", keep_for=timedelta(days=7), dry_run=True) + + # 실제 삭제 실행 + atio.expire_snapshots("users_table", keep_for=timedelta(days=7), dry_run=False) + +데이터베이스 연동 +---------------- + +Pandas와 Polars를 사용하여 데이터베이스에 안전하게 데이터를 저장할 수 있습니다. + +Pandas SQL 연동 +~~~~~~~~~~~~~~ + +.. code-block:: python + + import atio + import pandas as pd + from sqlalchemy import create_engine + + # 데이터베이스 연결 + engine = create_engine('postgresql://user:password@localhost/dbname') + + df = pd.DataFrame({ + "id": [1, 2, 3], + "name": ["Alice", "Bob", "Charlie"] + }) + + # SQL 데이터베이스에 저장 + atio.write(df, format="sql", name="users", con=engine) + +Polars 데이터베이스 연동 +~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + import atio + import polars as pl + + df = pl.DataFrame({ + "id": [1, 2, 3], + "name": ["Alice", "Bob", "Charlie"] + }) + + # 데이터베이스에 저장 + atio.write(df, format="database", + table_name="users", + connection_uri="postgresql://user:password@localhost/dbname") + +성능 최적화 +----------- + +진행도 표시 +~~~~~~~~~~ + +대용량 파일 처리 시 진행 상황을 실시간으로 확인할 수 있습니다. + +.. code-block:: python + + import atio + import pandas as pd + import numpy as np + + # 대용량 데이터 생성 + large_df = pd.DataFrame(np.random.randn(1000000, 10)) + + # 진행도 표시와 함께 저장 + atio.write(large_df, "large_data.parquet", + format="parquet", + show_progress=True) + +상세 로깅 +~~~~~~~~~ + +성능 진단을 위한 상세한 로깅을 활성화할 수 있습니다. + +.. code-block:: python + + # 상세한 성능 정보 출력 + atio.write(df, "data.parquet", format="parquet", verbose=True) + +이를 통해 다음과 같은 정보를 확인할 수 있습니다: + +- 각 단계별 소요 시간 +- 임시 파일 생성 및 교체 과정 +- 백업 및 롤백 과정 +- 성능 병목점 분석 + +에러 처리 +--------- + +Atio는 다양한 에러 상황에 대해 안전하게 처리합니다. + +파일 시스템 에러 +~~~~~~~~~~~~~~~ + +.. code-block:: python + + import atio + import pandas as pd + + df = pd.DataFrame({"a": [1, 2, 3]}) + + try: + # 권한이 없는 디렉토리에 저장 시도 + atio.write(df, "/root/data.parquet", format="parquet") + except PermissionError as e: + print(f"권한 에러: {e}") + # 원본 파일은 그대로 보존됨 + +포맷 에러 +~~~~~~~~~ + +.. code-block:: python + + try: + # 지원하지 않는 포맷 사용 + atio.write(df, "data.unknown", format="unknown") + except ValueError as e: + print(f"지원하지 않는 포맷: {e}") + +데이터베이스 에러 +~~~~~~~~~~~~~~~ + +.. code-block:: python + + try: + # 잘못된 데이터베이스 연결 정보 + atio.write(df, format="sql", + name="users", + con="invalid_connection") + except Exception as e: + print(f"데이터베이스 에러: {e}") + +플러그인 확장 +------------ + +Atio는 플러그인 아키텍처를 통해 새로운 형식을 쉽게 추가할 수 있습니다. + +커스텀 형식 등록 +~~~~~~~~~~~~~~~ + +.. code-block:: python + + from atio.plugins import register_writer + import pandas as pd + + # 커스텀 형식 등록 + def custom_writer(df, path, **kwargs): + # 커스텀 저장 로직 + with open(path, 'w') as f: + f.write("Custom format\n") + f.write(df.to_string()) + + # Pandas DataFrame에 대한 커스텀 형식 등록 + register_writer(pd.DataFrame, "custom", custom_writer) + + # 사용 + df = pd.DataFrame({"a": [1, 2, 3]}) + atio.write(df, "data.custom", format="custom") + +NumPy 배열 처리 +-------------- + +NumPy 배열의 다양한 저장 방식을 지원합니다. + +단일 배열 저장 +~~~~~~~~~~~~~ + +.. code-block:: python + + import atio + import numpy as np + + arr = np.array([[1, 2, 3], [4, 5, 6]]) + + # .npy 파일로 저장 + atio.write(arr, "array.npy", format="npy") + + # .csv 파일로 저장 + atio.write(arr, "array.csv", format="csv") + +여러 배열 저장 +~~~~~~~~~~~~~ + +.. code-block:: python + + # 여러 배열을 딕셔너리로 저장 + arrays = { + "features": np.random.randn(1000, 10), + "labels": np.random.randint(0, 2, 1000), + "metadata": np.array([1, 2, 3, 4, 5]) + } + + # 압축된 .npz 파일로 저장 + atio.write(arrays, "data.npz", format="npz_compressed") + +실제 사용 사례 +------------- + +머신러닝 파이프라인 +~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + import atio + import pandas as pd + from sklearn.model_selection import train_test_split + from sklearn.ensemble import RandomForestClassifier + + # 데이터 로드 및 전처리 + df = pd.read_csv("raw_data.csv") + + # 전처리된 데이터를 안전하게 저장 + atio.write(df, "processed_data.parquet", format="parquet") + + # 학습/테스트 분할 + X_train, X_test, y_train, y_test = train_test_split( + df.drop('target', axis=1), df['target'], test_size=0.2 + ) + + # 분할된 데이터를 스냅샷으로 저장 + atio.write_snapshot(X_train, "training_data", format="parquet") + atio.write_snapshot(X_test, "test_data", format="parquet") + + # 모델 학습 + model = RandomForestClassifier() + model.fit(X_train, y_train) + + # 예측 결과를 안전하게 저장 + predictions = model.predict(X_test) + results_df = pd.DataFrame({ + 'actual': y_test, + 'predicted': predictions + }) + + atio.write(results_df, "predictions.parquet", format="parquet") + +ETL 파이프라인 +~~~~~~~~~~~~~ + +.. code-block:: python + + import atio + import pandas as pd + from sqlalchemy import create_engine + + # 1. 원본 데이터 로드 + raw_data = pd.read_csv("source_data.csv") + + # 2. 데이터 정제 + cleaned_data = raw_data.dropna() + cleaned_data = cleaned_data[cleaned_data['value'] > 0] + + # 3. 안전하게 정제된 데이터 저장 + atio.write(cleaned_data, "cleaned_data.parquet", format="parquet") + + # 4. 데이터베이스에 저장 + engine = create_engine('postgresql://user:password@localhost/warehouse') + atio.write(cleaned_data, format="sql", + name="processed_table", + con=engine, + if_exists='replace') + + # 5. 스냅샷으로 버전 관리 + atio.write_snapshot(cleaned_data, "daily_processed", format="parquet") \ No newline at end of file diff --git a/docs/build/html/_sources/api.rst.txt b/docs/build/html/_sources/api.rst.txt new file mode 100644 index 0000000..2e0f4f9 --- /dev/null +++ b/docs/build/html/_sources/api.rst.txt @@ -0,0 +1,222 @@ +API 참조 +======== + +Atio의 모든 함수와 클래스에 대한 상세한 API 문서입니다. + +주요 함수 +-------- + +.. automodule:: atio + :members: + :undoc-members: + :show-inheritance: + +Core 모듈 +-------- + +.. automodule:: atio.core + :members: + :undoc-members: + :show-inheritance: + +write() +------- + +안전한 원자적 파일 쓰기를 수행하는 메인 함수입니다. + +.. function:: atio.write(obj, target_path=None, format='parquet', **kwargs) + + :param obj: 저장할 데이터 객체 (pandas.DataFrame, polars.DataFrame, numpy.ndarray) + :param target_path: 저장할 파일 경로 (파일 저장 시 필수) + :param format: 저장 형식 ('csv', 'parquet', 'excel', 'json', 'sql', 'database') + :param show_progress: 진행률 표시 여부 (기본값: False) + :param verbose: 상세 성능 정보 출력 여부 (기본값: False) + :param **kwargs: 형식별 추가 매개변수 + + :returns: None + + :raises: ValueError, IOError, DatabaseError + + **사용 예제:** + + .. code-block:: python + + import atio + import pandas as pd + + df = pd.DataFrame({"a": [1, 2, 3]}) + + # 기본 사용법 + atio.write(df, "data.parquet", format="parquet") + + # 진행률 표시 + atio.write(df, "data.parquet", format="parquet", show_progress=True) + + # 성능 정보 출력 + atio.write(df, "data.parquet", format="parquet", verbose=True) + +write_snapshot() +--------------- + +데이터 스냅샷을 생성하여 버전 관리를 수행합니다. + +.. function:: atio.write_snapshot(obj, table_name, format='parquet', **kwargs) + + :param obj: 저장할 데이터 객체 + :param table_name: 테이블 이름 (스냅샷 디렉토리명) + :param format: 저장 형식 + :param **kwargs: 추가 매개변수 + + :returns: 생성된 스냅샷 ID + + **사용 예제:** + + .. code-block:: python + + # 스냅샷 생성 + snapshot_id = atio.write_snapshot(df, "users", format="parquet") + print(f"생성된 스냅샷 ID: {snapshot_id}") + +read_table() +----------- + +스냅샷에서 데이터를 읽어옵니다. + +.. function:: atio.read_table(table_name, snapshot_id='latest', format='parquet', **kwargs) + + :param table_name: 테이블 이름 + :param snapshot_id: 스냅샷 ID (기본값: 'latest') + :param format: 읽을 형식 + :param **kwargs: 추가 매개변수 + + :returns: 데이터 객체 + + **사용 예제:** + + .. code-block:: python + + # 최신 스냅샷 읽기 + df = atio.read_table("users", format="parquet") + + # 특정 스냅샷 읽기 + df = atio.read_table("users", snapshot_id="20240101_120000", format="parquet") + +expire_snapshots() +----------------- + +오래된 스냅샷을 정리합니다. + +.. function:: atio.expire_snapshots(table_name, days=30, format='parquet') + + :param table_name: 테이블 이름 + :param days: 보관할 일수 (기본값: 30) + :param format: 형식 + + :returns: 삭제된 스냅샷 수 + + **사용 예제:** + + .. code-block:: python + + # 30일 이상 된 스냅샷 정리 + deleted_count = atio.expire_snapshots("users", days=30) + print(f"삭제된 스냅샷 수: {deleted_count}") + +Plugins 모듈 +----------- + +.. automodule:: atio.plugins + :members: + :undoc-members: + :show-inheritance: + +Utils 모듈 +--------- + +.. automodule:: atio.utils + :members: + :undoc-members: + :show-inheritance: + +지원하는 형식 +----------- + +CSV 형식 +~~~~~~~ + +.. code-block:: python + + atio.write(df, "data.csv", format="csv", index=False, encoding='utf-8') + +**지원 매개변수:** +- `index`: 인덱스 포함 여부 +- `encoding`: 인코딩 방식 +- `sep`: 구분자 +- `na_rep`: NA 값 표현 + +Parquet 형식 +~~~~~~~~~~~ + +.. code-block:: python + + atio.write(df, "data.parquet", format="parquet", compression='snappy') + +**지원 매개변수:** +- `compression`: 압축 방식 ('snappy', 'gzip', 'brotli') +- `engine`: 엔진 ('pyarrow', 'fastparquet') + +Excel 형식 +~~~~~~~~~ + +.. code-block:: python + + atio.write(df, "data.xlsx", format="excel", sheet_name="Sheet1") + +**지원 매개변수:** +- `sheet_name`: 시트 이름 +- `engine`: 엔진 ('openpyxl', 'xlsxwriter') + +JSON 형식 +~~~~~~~~~ + +.. code-block:: python + + atio.write(df, "data.json", format="json", orient="records") + +**지원 매개변수:** +- `orient`: 방향 ('records', 'split', 'index', 'columns', 'values', 'table') + +SQL 형식 +~~~~~~~~ + +.. code-block:: python + + atio.write(df, format="sql", name="table_name", con=engine, if_exists="replace") + +**지원 매개변수:** +- `name`: 테이블 이름 +- `con`: 데이터베이스 연결 +- `if_exists`: 테이블 존재 시 동작 ('fail', 'replace', 'append') + +예외 처리 +-------- + +Atio는 다음과 같은 예외를 발생시킬 수 있습니다: + +- **ValueError**: 잘못된 매개변수나 형식 +- **IOError**: 파일 시스템 오류 +- **DatabaseError**: 데이터베이스 연결 오류 +- **ImportError**: 필요한 패키지가 설치되지 않은 경우 + +**예외 처리 예제:** + +.. code-block:: python + + try: + atio.write(df, "data.parquet", format="parquet") + except ValueError as e: + print(f"매개변수 오류: {e}") + except IOError as e: + print(f"파일 시스템 오류: {e}") + except Exception as e: + print(f"예상치 못한 오류: {e}") \ No newline at end of file diff --git a/docs/build/html/_sources/api/_autosummary/atio.core.rst.txt b/docs/build/html/_sources/api/_autosummary/atio.core.rst.txt deleted file mode 100644 index 1e8cfa8..0000000 --- a/docs/build/html/_sources/api/_autosummary/atio.core.rst.txt +++ /dev/null @@ -1,12 +0,0 @@ -atio.core -========= - -.. automodule:: atio.core - - - .. rubric:: Functions - - .. autosummary:: - - write - \ No newline at end of file diff --git a/docs/build/html/_sources/api/_autosummary/atio.plugins.rst.txt b/docs/build/html/_sources/api/_autosummary/atio.plugins.rst.txt deleted file mode 100644 index b37467e..0000000 --- a/docs/build/html/_sources/api/_autosummary/atio.plugins.rst.txt +++ /dev/null @@ -1,13 +0,0 @@ -atio.plugins -============ - -.. automodule:: atio.plugins - - - .. rubric:: Functions - - .. autosummary:: - - get_writer - register_writer - \ No newline at end of file diff --git a/docs/build/html/_sources/api/_autosummary/atio.rst.txt b/docs/build/html/_sources/api/_autosummary/atio.rst.txt deleted file mode 100644 index 1364b66..0000000 --- a/docs/build/html/_sources/api/_autosummary/atio.rst.txt +++ /dev/null @@ -1,15 +0,0 @@ -atio -==== - -.. automodule:: atio - - -.. rubric:: Modules - -.. autosummary:: - :toctree: - :recursive: - - core - plugins - utils diff --git a/docs/build/html/_sources/api/_autosummary/atio.utils.rst.txt b/docs/build/html/_sources/api/_autosummary/atio.utils.rst.txt deleted file mode 100644 index 4fe785d..0000000 --- a/docs/build/html/_sources/api/_autosummary/atio.utils.rst.txt +++ /dev/null @@ -1,19 +0,0 @@ -atio.utils -========== - -.. automodule:: atio.utils - - - .. rubric:: Functions - - .. autosummary:: - - check_file_exists - setup_logger - - .. rubric:: Classes - - .. autosummary:: - - ProgressBar - \ No newline at end of file diff --git a/docs/build/html/_sources/api/modules.rst.txt b/docs/build/html/_sources/api/modules.rst.txt deleted file mode 100644 index 5b6eaf5..0000000 --- a/docs/build/html/_sources/api/modules.rst.txt +++ /dev/null @@ -1,8 +0,0 @@ -Modules -============= - -.. autosummary:: - :toctree: _autosummary - :recursive: - - atio diff --git a/docs/build/html/_sources/configuration.rst.txt b/docs/build/html/_sources/configuration.rst.txt new file mode 100644 index 0000000..6d805cc --- /dev/null +++ b/docs/build/html/_sources/configuration.rst.txt @@ -0,0 +1,491 @@ +설정 및 구성 +============ + +Atio의 다양한 설정 옵션과 구성 방법을 설명합니다. + +로깅 설정 +-------- + +Atio는 상세한 로깅을 통해 작업 과정을 추적할 수 있습니다. + +기본 로깅 +~~~~~~~~~ + +.. code-block:: python + + import atio + import pandas as pd + + df = pd.DataFrame({"a": [1, 2, 3]}) + + # 기본 로깅 (INFO 레벨) + atio.write(df, "data.parquet", format="parquet") + +**출력 예시:** +``` +[INFO] 임시 디렉토리 생성: /tmp/tmp12345 +[INFO] 임시 파일 경로: /tmp/tmp12345/data.parquet +[INFO] 사용할 writer: to_parquet (format: parquet) +[INFO] 데이터 임시 파일에 저장 완료: /tmp/tmp12345/data.parquet +[INFO] 원자적 교체 완료: /tmp/tmp12345/data.parquet -> data.parquet +[INFO] _SUCCESS 플래그 파일 생성: .data.parquet._SUCCESS +[INFO] ✅ Atomic write completed successfully (took 0.1234s) +``` + +상세 로깅 +~~~~~~~~~ + +.. code-block:: python + + # 상세한 성능 정보 출력 (DEBUG 레벨) + atio.write(df, "data.parquet", format="parquet", verbose=True) + +**출력 예시:** +``` +[INFO] 임시 디렉토리 생성: /tmp/tmp12345 +[INFO] 임시 파일 경로: /tmp/tmp12345/data.parquet +[INFO] 사용할 writer: to_parquet (format: parquet) +[INFO] 데이터 임시 파일에 저장 완료: /tmp/tmp12345/data.parquet +[INFO] 원자적 교체 완료: /tmp/tmp12345/data.parquet -> data.parquet +[INFO] _SUCCESS 플래그 파일 생성: .data.parquet._SUCCESS +[DEBUG] Atomic write step timings (SUCCESS): setup=0.0012s, write_call=0.0987s, replace=0.0001s, success_flag=0.0001s, total=0.1001s +[INFO] ✅ Atomic write completed successfully (took 0.1001s) +``` + +진행도 표시 +---------- + +대용량 파일 처리 시 진행 상황을 실시간으로 확인할 수 있습니다. + +기본 진행도 표시 +~~~~~~~~~~~~~~~ + +.. code-block:: python + + import atio + import pandas as pd + import numpy as np + + # 대용량 데이터 생성 + large_df = pd.DataFrame(np.random.randn(1000000, 10)) + + # 진행도 표시와 함께 저장 + atio.write(large_df, "large_data.parquet", + format="parquet", + show_progress=True) + +**출력 예시:** +``` +⠋ Writing large_data.parquet... [ 45.2 MB | 12.3 MB/s | 00:03 ] +⠙ Writing large_data.parquet... [ 67.8 MB | 11.9 MB/s | 00:05 ] +⠹ Writing large_data.parquet... [ 89.1 MB | 12.1 MB/s | 00:07 ] +✅ Writing completed successfully (89.1 MB in 7s) +``` + +진행도 표시 옵션 +~~~~~~~~~~~~~~~ + +진행도 표시는 다음과 같은 정보를 제공합니다: + +- **스피너**: 작업 진행 상태를 시각적으로 표시 +- **파일 크기**: 현재까지 저장된 데이터 크기 +- **처리 속도**: 초당 처리되는 데이터 양 +- **경과 시간**: 작업 시작 후 경과한 시간 + +성능 최적화 설정 +---------------- + +메모리 사용량 최적화 +~~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + # 대용량 데이터 처리 시 메모리 효율적인 설정 + atio.write(large_df, "data.parquet", format="parquet", + compression='snappy', # 빠른 압축 + index=False) # 인덱스 제외로 메모리 절약 + +압축 설정 +~~~~~~~~~ + +.. code-block:: python + + # 속도 우선 (압축 없음) + atio.write(df, "data.parquet", format="parquet", compression=None) + + # 균형 (snappy 압축) + atio.write(df, "data.parquet", format="parquet", compression='snappy') + + # 용량 우선 (gzip 압축) + atio.write(df, "data.parquet", format="parquet", compression='gzip') + +임시 디렉토리 설정 +----------------- + +기본적으로 Atio는 시스템의 임시 디렉토리를 사용합니다. + +사용자 정의 임시 디렉토리 +~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + import os + import tempfile + + # 임시 디렉토리 설정 + tempfile.tempdir = "/path/to/custom/temp" + + # 또는 환경 변수 설정 + os.environ['TMPDIR'] = "/path/to/custom/temp" + +**주의사항:** +- 임시 디렉토리는 충분한 디스크 공간이 있어야 합니다 +- 쓰기 권한이 있어야 합니다 +- 빠른 I/O 성능을 위해 SSD를 권장합니다 + +에러 처리 설정 +-------------- + +Atio는 다양한 에러 상황에 대해 안전하게 처리합니다. + +롤백 동작 +~~~~~~~~~ + +.. code-block:: python + + # 기본적으로 롤백이 자동으로 수행됩니다 + try: + atio.write(df, "data.parquet", format="parquet") + except Exception as e: + # 에러 발생 시 원본 파일은 보존됩니다 + print(f"저장 실패: {e}") + # 임시 파일은 자동으로 정리됩니다 + +백업 파일 관리 +~~~~~~~~~~~~~ + +.. code-block:: python + + # 백업 파일은 작업 성공 시 자동으로 삭제됩니다 + # 실패 시에는 롤백 후 삭제됩니다 + + # 백업 파일이 남아있는 경우 수동으로 확인 + import os + backup_file = "data.parquet._backup" + if os.path.exists(backup_file): + print("백업 파일이 존재합니다. 수동 확인이 필요할 수 있습니다.") + +완료 플래그 시스템 +------------------ + +Atio는 작업 완료를 확인할 수 있는 플래그 파일을 생성합니다. + +플래그 파일 확인 +~~~~~~~~~~~~~~~ + +.. code-block:: python + + import os + + # 저장 완료 후 플래그 파일 확인 + atio.write(df, "data.parquet", format="parquet") + + # 플래그 파일 경로 + flag_file = ".data.parquet._SUCCESS" + + if os.path.exists(flag_file): + print("저장이 성공적으로 완료되었습니다.") + else: + print("저장이 완료되지 않았거나 실패했습니다.") + +플래그 파일 활용 +~~~~~~~~~~~~~~~ + +.. code-block:: python + + # 배치 처리에서 완료 여부 확인 + files_to_process = ["data1.parquet", "data2.parquet", "data3.parquet"] + + for file in files_to_process: + flag_file = f".{file}._SUCCESS" + if not os.path.exists(flag_file): + print(f"{file} 처리가 완료되지 않았습니다.") + # 재처리 로직 + +스냅샷 설정 +---------- + +스냅샷 시스템의 다양한 설정 옵션을 설명합니다. + +스냅샷 모드 +~~~~~~~~~~ + +.. code-block:: python + + # overwrite 모드 (기본값) + atio.write_snapshot(df, "table_path", mode="overwrite", format="parquet") + + # append 모드 (기존 데이터에 추가) + atio.write_snapshot(df, "table_path", mode="append", format="parquet") + +스냅샷 정리 설정 +~~~~~~~~~~~~~~~ + +.. code-block:: python + + from datetime import timedelta + + # 7일 이상 된 스냅샷 삭제 + atio.expire_snapshots("table_path", + keep_for=timedelta(days=7), + dry_run=True) # 실제 삭제 전 확인 + + # 30일 이상 된 스냅샷 삭제 + atio.expire_snapshots("table_path", + keep_for=timedelta(days=30), + dry_run=False) # 실제 삭제 + +환경 변수 설정 +------------- + +Atio의 동작을 제어하는 환경 변수들을 설정할 수 있습니다. + +로깅 레벨 설정 +~~~~~~~~~~~~~ + +.. code-block:: python + + import os + + # DEBUG 레벨로 로깅 설정 + os.environ['ATIO_LOG_LEVEL'] = 'DEBUG' + + # INFO 레벨로 로깅 설정 (기본값) + os.environ['ATIO_LOG_LEVEL'] = 'INFO' + +임시 디렉토리 설정 +~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + # 임시 디렉토리 경로 설정 + os.environ['ATIO_TEMP_DIR'] = '/path/to/temp' + + # 또는 시스템 임시 디렉토리 설정 + os.environ['TMPDIR'] = '/path/to/temp' + +성능 모니터링 설정 +~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + # 성능 모니터링 활성화 + os.environ['ATIO_PERFORMANCE_MONITORING'] = 'true' + + # 성능 모니터링 비활성화 + os.environ['ATIO_PERFORMANCE_MONITORING'] = 'false' + +플러그인 설정 +------------ + +커스텀 플러그인을 등록하여 새로운 형식을 지원할 수 있습니다. + +플러그인 등록 +~~~~~~~~~~~~ + +.. code-block:: python + + from atio.plugins import register_writer + import pandas as pd + + # 커스텀 형식 등록 + def custom_writer(df, path, **kwargs): + # 커스텀 저장 로직 + with open(path, 'w') as f: + f.write("Custom format\n") + f.write(df.to_string()) + + # 등록 + register_writer(pd.DataFrame, "custom", custom_writer) + +플러그인 확인 +~~~~~~~~~~~~ + +.. code-block:: python + + from atio.plugins import WRITER_MAPPING + + # 등록된 플러그인 확인 + for obj_type, formats in WRITER_MAPPING.items(): + print(f"Object type: {obj_type.__name__}") + for fmt, handler in formats.items(): + print(f" - {fmt}: {handler}") + +설정 파일 사용 +------------- + +설정을 파일로 관리하여 일관된 설정을 유지할 수 있습니다. + +JSON 설정 파일 +~~~~~~~~~~~~~~ + +.. code-block:: python + + import json + + # 설정 파일 생성 + config = { + "default_format": "parquet", + "compression": "snappy", + "show_progress": True, + "verbose": False, + "temp_dir": "/path/to/temp" + } + + with open("atio_config.json", "w") as f: + json.dump(config, f, indent=2) + + # 설정 파일 읽기 + with open("atio_config.json", "r") as f: + config = json.load(f) + + # 설정 적용 + atio.write(df, "data.parquet", + format=config.get("default_format", "parquet"), + compression=config.get("compression", "snappy"), + show_progress=config.get("show_progress", False), + verbose=config.get("verbose", False)) + +YAML 설정 파일 +~~~~~~~~~~~~~ + +.. code-block:: python + + import yaml + + # 설정 파일 생성 + config = { + "default_format": "parquet", + "compression": "snappy", + "show_progress": True, + "verbose": False, + "temp_dir": "/path/to/temp" + } + + with open("atio_config.yaml", "w") as f: + yaml.dump(config, f) + + # 설정 파일 읽기 + with open("atio_config.yaml", "r") as f: + config = yaml.safe_load(f) + +모범 사례 +-------- + +프로덕션 환경 설정 +~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + # 프로덕션 환경을 위한 설정 + import os + import tempfile + + # 1. 전용 임시 디렉토리 설정 + tempfile.tempdir = "/var/tmp/atio" + os.makedirs(tempfile.tempdir, exist_ok=True) + + # 2. 로깅 레벨 설정 + os.environ['ATIO_LOG_LEVEL'] = 'INFO' + + # 3. 성능 최적화 설정 + def safe_write(df, path, **kwargs): + return atio.write(df, path, + format="parquet", + compression="snappy", + show_progress=True, + verbose=False, + **kwargs) + +개발 환경 설정 +~~~~~~~~~~~~~ + +.. code-block:: python + + # 개발 환경을 위한 설정 + import os + + # 1. 상세 로깅 활성화 + os.environ['ATIO_LOG_LEVEL'] = 'DEBUG' + + # 2. 성능 모니터링 활성화 + os.environ['ATIO_PERFORMANCE_MONITORING'] = 'true' + + # 3. 개발용 설정 + def dev_write(df, path, **kwargs): + return atio.write(df, path, + format="parquet", + compression=None, # 압축 없음으로 빠른 처리 + show_progress=True, + verbose=True, # 상세 정보 출력 + **kwargs) + +설정 검증 +-------- + +설정이 올바르게 적용되었는지 확인하는 방법을 설명합니다. + +기본 검증 +~~~~~~~~~ + +.. code-block:: python + + import atio + import pandas as pd + import tempfile + + # 테스트 데이터 생성 + df = pd.DataFrame({"test": [1, 2, 3]}) + + # 설정 테스트 + def test_config(): + # 임시 디렉토리 확인 + print(f"임시 디렉토리: {tempfile.gettempdir()}") + + # 로깅 테스트 + atio.write(df, "test.parquet", format="parquet", verbose=True) + + # 플래그 파일 확인 + import os + if os.path.exists(".test.parquet._SUCCESS"): + print("설정이 올바르게 작동합니다.") + else: + print("설정에 문제가 있을 수 있습니다.") + +성능 테스트 +~~~~~~~~~~ + +.. code-block:: python + + import time + import pandas as pd + import numpy as np + + # 성능 테스트 + def performance_test(): + # 대용량 데이터 생성 + large_df = pd.DataFrame(np.random.randn(100000, 10)) + + # 성능 측정 + start_time = time.time() + atio.write(large_df, "performance_test.parquet", + format="parquet", + show_progress=True) + end_time = time.time() + + print(f"처리 시간: {end_time - start_time:.2f}초") + + # 파일 크기 확인 + import os + file_size = os.path.getsize("performance_test.parquet") + print(f"파일 크기: {file_size / 1024 / 1024:.2f} MB") \ No newline at end of file diff --git a/docs/build/html/_sources/examples.rst.txt b/docs/build/html/_sources/examples.rst.txt new file mode 100644 index 0000000..64cd264 --- /dev/null +++ b/docs/build/html/_sources/examples.rst.txt @@ -0,0 +1,395 @@ +사용 예제 +========= + +Atio의 다양한 사용 사례와 고급 기능을 살펴보세요. + +기본 예제 +-------- + +간단한 데이터 저장 +~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + import atio + import pandas as pd + + # 샘플 데이터 생성 + data = { + "id": [1, 2, 3, 4, 5], + "name": ["Alice", "Bob", "Charlie", "Diana", "Eve"], + "age": [25, 30, 35, 28, 32], + "city": ["Seoul", "Busan", "Incheon", "Daegu", "Daejeon"], + "salary": [50000, 60000, 70000, 55000, 65000] + } + + df = pd.DataFrame(data) + + # 다양한 형식으로 저장 + atio.write(df, "employees.parquet", format="parquet") + atio.write(df, "employees.csv", format="csv", index=False) + atio.write(df, "employees.xlsx", format="excel", sheet_name="Employees") + +대용량 데이터 처리 +---------------- + +진행률 표시와 성능 모니터링 +~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + import numpy as np + import pandas as pd + import atio + + # 대용량 데이터 생성 (100만 행) + large_data = { + "id": range(1, 1000001), + "value": np.random.randn(1000000), + "category": np.random.choice(["A", "B", "C"], 1000000), + "timestamp": pd.date_range("2024-01-01", periods=1000000, freq="S") + } + + large_df = pd.DataFrame(large_data) + + # 진행률 표시와 성능 모니터링 활성화 + atio.write( + large_df, + "large_dataset.parquet", + format="parquet", + show_progress=True, + verbose=True, + compression='snappy' + ) + +데이터베이스 연동 +--------------- + +PostgreSQL 데이터베이스 저장 +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + import atio + import pandas as pd + from sqlalchemy import create_engine + + # 데이터베이스 연결 + engine = create_engine('postgresql://username:password@localhost:5432/mydb') + + # 샘플 데이터 + sales_data = { + "order_id": [1001, 1002, 1003, 1004, 1005], + "product_name": ["Laptop", "Mouse", "Keyboard", "Monitor", "Headphones"], + "quantity": [1, 2, 1, 1, 3], + "price": [1200, 25, 75, 300, 150], + "order_date": pd.date_range("2024-01-01", periods=5) + } + + sales_df = pd.DataFrame(sales_data) + + # 데이터베이스에 안전하게 저장 + atio.write( + sales_df, + format="sql", + name="sales_orders", + con=engine, + if_exists="replace", + index=False + ) + +스냅샷 기반 버전 관리 +------------------- + +데이터 버전 관리 +~~~~~~~~~~~~~~~ + +.. code-block:: python + + import atio + import pandas as pd + from datetime import datetime, timedelta + + # 초기 데이터 + initial_data = { + "user_id": [1, 2, 3], + "name": ["Alice", "Bob", "Charlie"], + "status": ["active", "active", "inactive"] + } + + df = pd.DataFrame(initial_data) + + # 초기 스냅샷 생성 + snapshot_id_1 = atio.write_snapshot(df, "users", format="parquet") + print(f"초기 스냅샷 생성: {snapshot_id_1}") + + # 데이터 업데이트 + df.loc[df['user_id'] == 3, 'status'] = 'active' + df = df.append({"user_id": 4, "name": "Diana", "status": "active"}, ignore_index=True) + + # 업데이트된 스냅샷 생성 + snapshot_id_2 = atio.write_snapshot(df, "users", format="parquet") + print(f"업데이트 스냅샷 생성: {snapshot_id_2}") + + # 최신 데이터 읽기 + latest_df = atio.read_table("users", format="parquet") + print("최신 데이터:") + print(latest_df) + + # 특정 스냅샷 읽기 + initial_df = atio.read_table("users", snapshot_id=snapshot_id_1, format="parquet") + print("초기 데이터:") + print(initial_df) + + # 오래된 스냅샷 정리 (7일 이상) + deleted_count = atio.expire_snapshots("users", days=7, format="parquet") + print(f"삭제된 스냅샷 수: {deleted_count}") + +Polars DataFrame 활용 +------------------- + +고성능 데이터 처리 +~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + import atio + import polars as pl + import numpy as np + + # Polars DataFrame 생성 + polars_data = { + "id": range(1, 10001), + "value": np.random.randn(10000), + "category": np.random.choice(["A", "B", "C", "D"], 10000), + "score": np.random.uniform(0, 100, 10000) + } + + polars_df = pl.DataFrame(polars_data) + + # Polars DataFrame 저장 + atio.write( + polars_df, + "polars_data.parquet", + format="parquet", + compression='snappy', + show_progress=True + ) + + # 데이터 변환 후 저장 + filtered_df = polars_df.filter(pl.col("score") > 50) + aggregated_df = filtered_df.group_by("category").agg( + pl.col("value").mean().alias("avg_value"), + pl.col("score").mean().alias("avg_score") + ) + + atio.write(aggregated_df, "aggregated_data.parquet", format="parquet") + +에러 처리 및 복구 +--------------- + +안전한 데이터 처리 +~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + import atio + import pandas as pd + import os + + def safe_data_processing(): + try: + # 원본 파일이 있는지 확인 + if os.path.exists("important_data.parquet"): + print("원본 파일이 존재합니다.") + + # 데이터 처리 및 저장 + df = pd.DataFrame({ + "id": [1, 2, 3], + "data": ["important", "data", "here"] + }) + + atio.write(df, "important_data.parquet", format="parquet") + print("데이터가 안전하게 저장되었습니다.") + + # SUCCESS 파일 확인 + if os.path.exists("important_data.parquet_SUCCESS"): + print("저장 완료 플래그가 생성되었습니다.") + + except Exception as e: + print(f"오류 발생: {e}") + print("원본 파일이 보존되었습니다.") + + # 임시 파일 정리 + temp_files = [f for f in os.listdir(".") if f.startswith("important_data.parquet.tmp")] + for temp_file in temp_files: + try: + os.remove(temp_file) + print(f"임시 파일 정리: {temp_file}") + except: + pass + + # 안전한 데이터 처리 실행 + safe_data_processing() + +배치 처리 +------- + +여러 파일 동시 처리 +~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + import atio + import pandas as pd + import os + from pathlib import Path + + def process_multiple_files(): + # 처리할 파일 목록 + files_to_process = [ + {"name": "users", "data": pd.DataFrame({"id": [1, 2], "name": ["Alice", "Bob"]})}, + {"name": "products", "data": pd.DataFrame({"id": [1, 2], "product": ["Laptop", "Mouse"]})}, + {"name": "orders", "data": pd.DataFrame({"id": [1, 2], "amount": [100, 200]})} + ] + + # 각 파일을 안전하게 처리 + for file_info in files_to_process: + try: + file_path = f"{file_info['name']}.parquet" + atio.write( + file_info['data'], + file_path, + format="parquet", + show_progress=True + ) + print(f"{file_info['name']} 파일 처리 완료") + + except Exception as e: + print(f"{file_info['name']} 파일 처리 실패: {e}") + continue + + # 배치 처리 실행 + process_multiple_files() + +성능 최적화 +--------- + +압축 및 최적화 설정 +~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + import atio + import pandas as pd + import numpy as np + + # 대용량 데이터 생성 + large_df = pd.DataFrame({ + "id": range(1, 100001), + "value": np.random.randn(100000), + "text": ["sample text"] * 100000 + }) + + # 다양한 압축 설정으로 성능 비교 + compression_settings = [ + ("snappy", "fast_compression.parquet"), + ("gzip", "balanced_compression.parquet"), + ("brotli", "high_compression.parquet") + ] + + for compression, filename in compression_settings: + print(f"\n{compression} 압축으로 저장 중...") + atio.write( + large_df, + filename, + format="parquet", + compression=compression, + show_progress=True, + verbose=True + ) + + # 파일 크기 확인 + file_size = os.path.getsize(filename) / (1024 * 1024) # MB + print(f"파일 크기: {file_size:.2f} MB") + +실제 사용 사례 +------------ + +데이터 파이프라인 +~~~~~~~~~~~~~~~ + +.. code-block:: python + + import atio + import pandas as pd + from datetime import datetime, timedelta + + class DataPipeline: + def __init__(self, base_path="data"): + self.base_path = Path(base_path) + self.base_path.mkdir(exist_ok=True) + + def extract_data(self): + """데이터 추출 (시뮬레이션)""" + # 실제로는 API나 데이터베이스에서 데이터를 가져옴 + data = { + "timestamp": pd.date_range(datetime.now(), periods=1000, freq="H"), + "value": np.random.randn(1000), + "source": ["api"] * 1000 + } + return pd.DataFrame(data) + + def transform_data(self, df): + """데이터 변환""" + # 시간별 집계 + df['hour'] = df['timestamp'].dt.hour + df['day'] = df['timestamp'].dt.date + + # 일별 통계 + daily_stats = df.groupby('day').agg({ + 'value': ['mean', 'std', 'min', 'max'] + }).round(2) + + return daily_stats + + def load_data(self, df, table_name): + """데이터 로드""" + # 스냅샷 생성 + snapshot_id = atio.write_snapshot( + df, + table_name, + format="parquet" + ) + + # 최신 데이터도 별도 저장 + latest_path = self.base_path / f"{table_name}_latest.parquet" + atio.write(df, str(latest_path), format="parquet") + + return snapshot_id + + def run_pipeline(self): + """파이프라인 실행""" + print("데이터 파이프라인 시작...") + + # 1. 데이터 추출 + raw_data = self.extract_data() + print(f"추출된 데이터: {len(raw_data)} 행") + + # 2. 데이터 변환 + processed_data = self.transform_data(raw_data) + print(f"처리된 데이터: {len(processed_data)} 행") + + # 3. 데이터 로드 + snapshot_id = self.load_data(processed_data, "daily_stats") + print(f"스냅샷 생성 완료: {snapshot_id}") + + # 4. 오래된 스냅샷 정리 + deleted_count = atio.expire_snapshots("daily_stats", days=30) + print(f"정리된 스냅샷: {deleted_count}개") + + print("파이프라인 완료!") + + # 파이프라인 실행 + pipeline = DataPipeline() + pipeline.run_pipeline() \ No newline at end of file diff --git a/docs/build/html/_sources/formats.rst.txt b/docs/build/html/_sources/formats.rst.txt new file mode 100644 index 0000000..488620c --- /dev/null +++ b/docs/build/html/_sources/formats.rst.txt @@ -0,0 +1,456 @@ +지원하는 파일 형식 +================== + +Atio는 다양한 데이터 형식을 지원합니다. 각 형식별로 지원하는 라이브러리와 사용법을 설명합니다. + +Pandas DataFrame 형식 +-------------------- + +Pandas DataFrame은 가장 많은 형식을 지원합니다. + +CSV (Comma-Separated Values) +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + import atio + import pandas as pd + + df = pd.DataFrame({ + "name": ["Alice", "Bob", "Charlie"], + "age": [25, 30, 35], + "city": ["Seoul", "Busan", "Incheon"] + }) + + # 기본 CSV 저장 + atio.write(df, "users.csv", format="csv") + + # 추가 옵션과 함께 저장 + atio.write(df, "users.csv", format="csv", + index=False, + encoding='utf-8', + sep=';') + +**지원 옵션:** +- `index`: 인덱스 포함 여부 (기본값: True) +- `encoding`: 인코딩 방식 (기본값: 'utf-8') +- `sep`: 구분자 (기본값: ',') +- `header`: 헤더 포함 여부 (기본값: True) + +Parquet +~~~~~~~ + +.. code-block:: python + + # Parquet 형식으로 저장 + atio.write(df, "users.parquet", format="parquet") + + # 압축 옵션과 함께 저장 + atio.write(df, "users.parquet", format="parquet", + compression='snappy') + +**지원 옵션:** +- `compression`: 압축 방식 ('snappy', 'gzip', 'brotli', None) +- `index`: 인덱스 포함 여부 (기본값: True) + +Excel +~~~~~ + +.. code-block:: python + + # Excel 파일로 저장 + atio.write(df, "users.xlsx", format="excel") + + # 시트명과 옵션 지정 + atio.write(df, "users.xlsx", format="excel", + sheet_name="Users", + index=False) + +**지원 옵션:** +- `sheet_name`: 시트 이름 (기본값: 'Sheet1') +- `index`: 인덱스 포함 여부 (기본값: True) +- `engine`: 엔진 ('openpyxl', 'xlsxwriter') + +**필요 라이브러리:** `pip install openpyxl` + +JSON +~~~~ + +.. code-block:: python + + # JSON 형식으로 저장 + atio.write(df, "users.json", format="json") + + # 들여쓰기와 함께 저장 + atio.write(df, "users.json", format="json", + indent=2, + orient='records') + +**지원 옵션:** +- `orient`: JSON 구조 ('split', 'records', 'index', 'columns', 'values', 'table') +- `indent`: 들여쓰기 크기 +- `date_format`: 날짜 형식 + +Pickle +~~~~~~ + +.. code-block:: python + + # Pickle 형식으로 저장 + atio.write(df, "users.pkl", format="pickle") + + # 압축과 함께 저장 + atio.write(df, "users.pkl", format="pickle", + compression='gzip') + +**지원 옵션:** +- `compression`: 압축 방식 ('gzip', 'bz2', 'xz', None) + +HTML +~~~~ + +.. code-block:: python + + # HTML 테이블로 저장 + atio.write(df, "users.html", format="html") + + # 스타일과 함께 저장 + atio.write(df, "users.html", format="html", + index=False, + classes='table table-striped') + +**지원 옵션:** +- `classes`: CSS 클래스 +- `index`: 인덱스 포함 여부 (기본값: True) + +SQL +~~~ + +.. code-block:: python + + import atio + import pandas as pd + from sqlalchemy import create_engine + + # 데이터베이스 연결 + engine = create_engine('postgresql://user:password@localhost/dbname') + + df = pd.DataFrame({ + "id": [1, 2, 3], + "name": ["Alice", "Bob", "Charlie"] + }) + + # SQL 데이터베이스에 저장 + atio.write(df, format="sql", + name="users", + con=engine, + if_exists='replace') + +**지원 옵션:** +- `name`: 테이블 이름 (필수) +- `con`: 데이터베이스 연결 객체 (필수) +- `if_exists`: 테이블이 존재할 때 동작 ('fail', 'replace', 'append') +- `index`: 인덱스를 컬럼으로 저장 여부 (기본값: True) + +**필요 라이브러리:** `pip install sqlalchemy` + +Polars DataFrame 형식 +-------------------- + +Polars는 빠른 데이터 처리를 위한 현대적인 DataFrame 라이브러리입니다. + +CSV +~~~ + +.. code-block:: python + + import atio + import polars as pl + + df = pl.DataFrame({ + "name": ["Alice", "Bob", "Charlie"], + "age": [25, 30, 35], + "city": ["Seoul", "Busan", "Incheon"] + }) + + # CSV 저장 + atio.write(df, "users.csv", format="csv") + + # 구분자와 함께 저장 + atio.write(df, "users.csv", format="csv", + separator=';') + +**지원 옵션:** +- `separator`: 구분자 (기본값: ',') +- `include_header`: 헤더 포함 여부 (기본값: True) + +Parquet +~~~~~~~ + +.. code-block:: python + + # Parquet 저장 + atio.write(df, "users.parquet", format="parquet") + + # 압축과 함께 저장 + atio.write(df, "users.parquet", format="parquet", + compression="snappy") + +**지원 옵션:** +- `compression`: 압축 방식 ('snappy', 'gzip', 'brotli', 'lz4raw', 'zstd', None) + +JSON +~~~~ + +.. code-block:: python + + # JSON 저장 + atio.write(df, "users.json", format="json") + + # 파일별 저장 + atio.write(df, "users.json", format="json", + file=True) + +**지원 옵션:** +- `file`: 파일별 저장 여부 (기본값: False) + +IPC (Arrow) +~~~~~~~~~~~ + +.. code-block:: python + + # IPC (Arrow) 형식으로 저장 + atio.write(df, "users.arrow", format="ipc") + + # 압축과 함께 저장 + atio.write(df, "users.arrow", format="ipc", + compression="lz4") + +**지원 옵션:** +- `compression`: 압축 방식 ('lz4', 'zstd', None) + +Avro +~~~~ + +.. code-block:: python + + # Avro 형식으로 저장 + atio.write(df, "users.avro", format="avro") + +**필요 라이브러리:** `pip install fastavro` + +Excel +~~~~~ + +.. code-block:: python + + # Excel 저장 + atio.write(df, "users.xlsx", format="excel") + +**필요 라이브러리:** `pip install xlsx2csv openpyxl` + +Database +~~~~~~~~ + +.. code-block:: python + + # 데이터베이스에 저장 + atio.write(df, format="database", + table_name="users", + connection_uri="postgresql://user:password@localhost/dbname") + +**지원 옵션:** +- `table_name`: 테이블 이름 (필수) +- `connection_uri`: 데이터베이스 연결 URI (필수) + +**필요 라이브러리:** `pip install connectorx` + +NumPy 배열 형식 +-------------- + +NumPy 배열은 수치 데이터 처리에 최적화된 형식을 지원합니다. + +NPY (NumPy Binary) +~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + import atio + import numpy as np + + arr = np.array([[1, 2, 3], [4, 5, 6]]) + + # .npy 파일로 저장 + atio.write(arr, "array.npy", format="npy") + +**특징:** +- 단일 배열을 효율적으로 저장 +- 메타데이터와 함께 저장 +- 빠른 읽기/쓰기 속도 + +NPZ (NumPy Compressed) +~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + # 여러 배열을 딕셔너리로 저장 + arrays = { + "features": np.random.randn(1000, 10), + "labels": np.random.randint(0, 2, 1000), + "metadata": np.array([1, 2, 3, 4, 5]) + } + + # 압축되지 않은 .npz 파일로 저장 + atio.write(arrays, "data.npz", format="npz") + + # 압축된 .npz 파일로 저장 + atio.write(arrays, "data.npz", format="npz_compressed") + +**특징:** +- 여러 배열을 하나의 파일에 저장 +- 압축 옵션으로 저장 공간 절약 +- 딕셔너리 형태로 데이터 구조화 + +CSV +~~~ + +.. code-block:: python + + # CSV로 저장 + atio.write(arr, "array.csv", format="csv") + + # 구분자와 함께 저장 + atio.write(arr, "array.csv", format="csv", + delimiter=';', + fmt='%.2f') + +**지원 옵션:** +- `delimiter`: 구분자 (기본값: ',') +- `fmt`: 숫자 형식 (예: '%.2f', '%.4e') +- `header`: 헤더 포함 여부 +- `comments`: 주석 문자 + +형식별 성능 비교 +---------------- + +다양한 형식의 성능 특성을 비교해보겠습니다. + +속도 비교 +~~~~~~~~~ + +1. **가장 빠른 형식:** + - NumPy: `.npy`, `.npz` + - Polars: `.ipc` (Arrow) + - Pandas: `.parquet` (snappy 압축) + +2. **중간 속도:** + - CSV (단순한 구조) + - JSON (중간 복잡도) + +3. **상대적으로 느린 형식:** + - Excel (복잡한 구조) + - Pickle (Python 특화) + +용량 비교 +~~~~~~~~~ + +1. **가장 작은 용량:** + - `.parquet` (컬럼 기반 압축) + - `.npz_compressed` (압축된 NumPy) + - `.ipc` (Arrow 압축) + +2. **중간 용량:** + - `.npy` (단일 배열) + - JSON (텍스트 기반) + +3. **상대적으로 큰 용량:** + - CSV (텍스트 기반) + - Excel (복잡한 구조) + +호환성 비교 +~~~~~~~~~~~ + +1. **최고 호환성:** + - CSV (모든 시스템에서 지원) + - JSON (웹 표준) + +2. **좋은 호환성:** + - Excel (비즈니스 환경) + - Parquet (빅데이터 생태계) + +3. **제한적 호환성:** + - `.npy`/`.npz` (Python/NumPy 특화) + - `.ipc` (Arrow 생태계) + +권장 사용 사례 +------------- + +데이터 분석 및 머신러닝 +~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + # 학습 데이터: 빠른 읽기/쓰기를 위해 Parquet 사용 + atio.write(training_data, "train.parquet", format="parquet") + + # 모델 가중치: NumPy 배열로 저장 + atio.write(model_weights, "weights.npy", format="npy") + + # 실험 결과: JSON으로 저장 (가독성) + atio.write(results, "experiment_results.json", format="json") + +웹 애플리케이션 +~~~~~~~~~~~~~~ + +.. code-block:: python + + # API 응답: JSON 형식 + atio.write(api_data, "response.json", format="json") + + # 대용량 데이터: Parquet 형식 + atio.write(large_dataset, "dataset.parquet", format="parquet") + +데이터 파이프라인 +~~~~~~~~~~~~~~~~ + +.. code-block:: python + + # 중간 결과: 빠른 처리를 위해 IPC 사용 + atio.write(intermediate_data, "step1.arrow", format="ipc") + + # 최종 결과: 호환성을 위해 CSV 사용 + atio.write(final_data, "output.csv", format="csv") + +비즈니스 보고서 +~~~~~~~~~~~~~~ + +.. code-block:: python + + # 엑셀 보고서 + atio.write(report_data, "monthly_report.xlsx", format="excel") + + # HTML 대시보드 + atio.write(dashboard_data, "dashboard.html", format="html") + +형식 확장하기 +------------ + +새로운 형식을 추가하려면 플러그인 시스템을 사용하세요. + +.. code-block:: python + + from atio.plugins import register_writer + import pandas as pd + + # 커스텀 형식 등록 + def yaml_writer(df, path, **kwargs): + import yaml + data = df.to_dict('records') + with open(path, 'w') as f: + yaml.dump(data, f, **kwargs) + + # 등록 + register_writer(pd.DataFrame, "yaml", yaml_writer) + + # 사용 + atio.write(df, "data.yaml", format="yaml") \ No newline at end of file diff --git a/docs/build/html/_sources/index.md.txt b/docs/build/html/_sources/index.md.txt deleted file mode 100644 index ad04586..0000000 --- a/docs/build/html/_sources/index.md.txt +++ /dev/null @@ -1,12 +0,0 @@ -# Atio Documentation - -```{include} ../../README.md -:relative-images: -``` - -```{toctree} -:maxdepth: 2 -:caption: API Reference - -api/modules -``` diff --git a/docs/build/html/_sources/index.rst.txt b/docs/build/html/_sources/index.rst.txt new file mode 100644 index 0000000..b078eb6 --- /dev/null +++ b/docs/build/html/_sources/index.rst.txt @@ -0,0 +1,165 @@ +Atio Documentation +================== + +**Atio** 🛡️는 안전하고 원자적인 파일 쓰기를 지원하는 경량 Python 라이브러리입니다. + +Pandas, Polars, NumPy 등 데이터 객체 저장 시 **파일 손상 없이**, **트랜잭션처럼 안전하게 처리**할 수 있습니다. + +주요 기능 +--------- + +🔒 **Atomic File Writing** + Safe writing using temporary files + +📊 **Multiple Format Support** + CSV, Parquet, Excel, JSON, etc. + +🗄️ **Database Support** + Direct SQL and Database writing + +📈 **Progress Display** + Progress monitoring for large data processing + +🔄 **Rollback Function** + Automatic recovery when errors occur + +🎯 **Simple API** + Intuitive and easy-to-use interface + +📋 **Version Management** + Snapshot-based data version management + +🧹 **Auto Cleanup** + Automatic deletion of old data + +빠른 시작 +--------- + +.. code-block:: python + + import atio + import pandas as pd + + # 간단한 DataFrame 생성 + df = pd.DataFrame({ + "name": ["Alice", "Bob", "Charlie"], + "age": [25, 30, 35], + "city": ["Seoul", "Busan", "Incheon"] + }) + + # 안전한 파일 쓰기 + atio.write(df, "users.parquet", format="parquet") + + # 진행도 표시와 함께 저장 + atio.write(df, "users.csv", format="csv", show_progress=True) + +지원하는 형식 +------------ + +.. list-table:: 지원하는 파일 형식 + :widths: 20 20 20 20 + :header-rows: 1 + + * - 형식 + - Pandas + - Polars + - NumPy + * - CSV + - ✅ + - ✅ + - ✅ + * - Parquet + - ✅ + - ✅ + - ❌ + * - Excel + - ✅ + - ✅ + - ❌ + * - JSON + - ✅ + - ✅ + - ❌ + * - Pickle + - ✅ + - ❌ + - ❌ + * - HTML + - ✅ + - ❌ + - ❌ + * - SQL + - ✅ + - ❌ + - ❌ + * - Database + - ❌ + - ✅ + - ❌ + * - NPY/NPZ + - ❌ + - ❌ + - ✅ + +사용 사례 +-------- + +🔹 **데이터 파이프라인** + ETL 과정에서 중간 데이터 안전하게 저장 + +🔹 **실험 데이터 관리** + 머신러닝 실험 결과의 버전 관리 + +🔹 **대용량 데이터 처리** + 대용량 파일의 안전한 저장 및 진행도 모니터링 + +🔹 **데이터베이스 연동** + Pandas/Polars 데이터를 SQL/NoSQL DB에 안전하게 저장 + +목차 +---- + +.. toctree:: + :maxdepth: 2 + :caption: 사용자 가이드: + + installation + quickstart + examples + advanced_usage + +.. toctree:: + :maxdepth: 2 + :caption: API 참조: + + api + configuration + +API Reference +============= + +핵심 함수들 +----------- + +.. automodule:: atio + :members: + :undoc-members: + +.. automodule:: atio.core + :members: + :undoc-members: + +.. automodule:: atio.plugins + :members: + :undoc-members: + +.. automodule:: atio.utils + :members: + :undoc-members: + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` diff --git a/docs/build/html/_sources/installation.rst.txt b/docs/build/html/_sources/installation.rst.txt new file mode 100644 index 0000000..eb0162b --- /dev/null +++ b/docs/build/html/_sources/installation.rst.txt @@ -0,0 +1,199 @@ +설치 가이드 +========== + +Atio를 설치하는 방법을 안내합니다. + +PyPI를 통한 설치 +---------------- + +가장 간단한 설치 방법은 PyPI를 사용하는 것입니다: + +.. code-block:: bash + + pip install atio + +특정 버전 설치 +~~~~~~~~~~~~~ + +특정 버전을 설치하려면: + +.. code-block:: bash + + pip install atio==2.0.0 + +개발 버전 설치 +~~~~~~~~~~~~~ + +최신 개발 버전을 설치하려면: + +.. code-block:: bash + + pip install git+https://github.com/seojaeohcode/atio.git + +의존성 +------ + +필수 의존성 +~~~~~~~~~~ + +- Python 3.8+ +- pandas +- numpy + +선택적 의존성 +~~~~~~~~~~~~ + +특정 형식을 사용하려면 추가 라이브러리가 필요합니다: + +**Parquet 형식:** +.. code-block:: bash + + pip install pyarrow + # 또는 + pip install fastparquet + +**Excel 형식:** +.. code-block:: bash + + pip install openpyxl + # 또는 + pip install xlsxwriter + +**SQL 데이터베이스:** +.. code-block:: bash + + pip install sqlalchemy + +**Polars 지원:** +.. code-block:: bash + + pip install polars + +**Avro 형식 (Polars):** +.. code-block:: bash + + pip install fastavro + +**Polars Excel 지원:** +.. code-block:: bash + + pip install xlsx2csv openpyxl + +**Polars 데이터베이스 지원:** +.. code-block:: bash + + pip install connectorx + +모든 의존성 설치 +~~~~~~~~~~~~~~~ + +모든 기능을 사용하려면: + +.. code-block:: bash + + pip install atio[all] + +또는 개별적으로: + +.. code-block:: bash + + pip install atio + pip install pyarrow openpyxl sqlalchemy polars fastavro xlsx2csv connectorx + +환경 확인 +--------- + +설치가 완료되었는지 확인하려면: + +.. code-block:: python + + import atio + print(f"Atio 버전: {atio.__version__}") + + # 기본 기능 테스트 + import pandas as pd + df = pd.DataFrame({"test": [1, 2, 3]}) + atio.write(df, "test.parquet", format="parquet") + print("설치가 성공적으로 완료되었습니다!") + +가상환경 사용 권장 +----------------- + +프로젝트별로 독립적인 환경을 유지하기 위해 가상환경 사용을 권장합니다: + +.. code-block:: bash + + # 가상환경 생성 + python -m venv atio_env + + # 가상환경 활성화 (Windows) + atio_env\Scripts\activate + + # 가상환경 활성화 (macOS/Linux) + source atio_env/bin/activate + + # Atio 설치 + pip install atio + +Conda 사용 +--------- + +Conda를 사용하는 경우: + +.. code-block:: bash + + # Conda 환경 생성 + conda create -n atio_env python=3.8 + conda activate atio_env + + # Atio 설치 + pip install atio + +문제 해결 +--------- + +설치 중 문제가 발생하는 경우: + +**권한 오류:** +.. code-block:: bash + + pip install atio --user + +**캐시 문제:** +.. code-block:: bash + + pip install atio --no-cache-dir + +**의존성 충돌:** +.. code-block:: bash + + pip install atio --force-reinstall + +**특정 Python 버전:** +.. code-block:: bash + + python3.8 -m pip install atio + +업그레이드 +---------- + +최신 버전으로 업그레이드: + +.. code-block:: bash + + pip install --upgrade atio + +특정 버전으로 다운그레이드: + +.. code-block:: bash + + pip install atio==1.0.0 + +제거 +---- + +Atio를 제거하려면: + +.. code-block:: bash + + pip uninstall atio \ No newline at end of file diff --git a/docs/build/html/_sources/quickstart.rst.txt b/docs/build/html/_sources/quickstart.rst.txt new file mode 100644 index 0000000..12b7060 --- /dev/null +++ b/docs/build/html/_sources/quickstart.rst.txt @@ -0,0 +1,126 @@ +빠른 시작 +========== + +Atio를 사용하여 안전한 파일 쓰기를 시작해보세요. + +기본 사용법 +---------- + +가장 간단한 사용법부터 시작해보겠습니다: + +.. code-block:: python + + import atio + import pandas as pd + + # 샘플 데이터 생성 + df = pd.DataFrame({ + "name": ["Alice", "Bob", "Charlie"], + "age": [25, 30, 35], + "city": ["Seoul", "Busan", "Incheon"] + }) + + # 안전한 파일 쓰기 + atio.write(df, "users.parquet", format="parquet") + +이 코드는 다음과 같은 안전장치를 제공합니다: + +- ✅ 임시 파일에 먼저 저장 +- ✅ 저장 완료 후 원자적으로 파일 교체 +- ✅ 실패 시 원본 파일 보존 +- ✅ `_SUCCESS` 플래그 파일 생성 + +다양한 형식 지원 +--------------- + +Atio는 다양한 데이터 형식을 지원합니다: + +.. code-block:: python + + # CSV 형식 + atio.write(df, "users.csv", format="csv", index=False) + + # Excel 형식 + atio.write(df, "users.xlsx", format="excel", sheet_name="Users") + + # JSON 형식 + atio.write(df, "users.json", format="json", orient="records") + + # Parquet 형식 (권장) + atio.write(df, "users.parquet", format="parquet") + +진행률 표시 +---------- + +대용량 데이터 처리 시 진행률을 확인할 수 있습니다: + +.. code-block:: python + + # 진행률 표시 활성화 + atio.write(large_df, "big_data.parquet", format="parquet", show_progress=True) + +성능 모니터링 +------------ + +상세한 성능 정보를 확인하려면: + +.. code-block:: python + + # 성능 정보 출력 + atio.write(df, "data.parquet", format="parquet", verbose=True) + +Polars DataFrame 지원 +------------------- + +Polars DataFrame도 지원합니다: + +.. code-block:: python + + import polars as pl + + # Polars DataFrame 생성 + polars_df = pl.DataFrame({ + "a": [1, 2, 3], + "b": [4, 5, 6] + }) + + # Polars DataFrame 저장 + atio.write(polars_df, "data.parquet", format="parquet") + +데이터베이스 저장 +--------------- + +SQL 데이터베이스에 직접 저장할 수도 있습니다: + +.. code-block:: python + + from sqlalchemy import create_engine + + # 데이터베이스 연결 + engine = create_engine('postgresql://user:password@localhost/dbname') + + # 데이터베이스에 저장 + atio.write(df, format="sql", name="users", con=engine, if_exists="replace") + +스냅샷 기능 +---------- + +데이터 버전 관리를 위한 스냅샷 기능: + +.. code-block:: python + + # 스냅샷 생성 + atio.write_snapshot(df, "users", format="parquet") + + # 스냅샷 읽기 + df = atio.read_table("users", snapshot_id="latest") + + # 오래된 스냅샷 정리 + atio.expire_snapshots("users", days=30) + +다음 단계 +-------- + +- :doc:`api` - 전체 API 참조 +- :doc:`examples` - 고급 사용 예제 +- :doc:`installation` - 설치 가이드 \ No newline at end of file diff --git a/docs/build/html/_static/custom.css b/docs/build/html/_static/custom.css new file mode 100644 index 0000000..b5198bd --- /dev/null +++ b/docs/build/html/_static/custom.css @@ -0,0 +1 @@ +/* 기본 상태로 복원 - 커스텀 스타일 제거 */ \ No newline at end of file diff --git a/docs/build/html/_static/documentation_options.js b/docs/build/html/_static/documentation_options.js index 7e4c114..03dec6c 100644 --- a/docs/build/html/_static/documentation_options.js +++ b/docs/build/html/_static/documentation_options.js @@ -1,6 +1,6 @@ const DOCUMENTATION_OPTIONS = { - VERSION: '', - LANGUAGE: 'en', + VERSION: '2.0.0', + LANGUAGE: 'ko', COLLAPSE_INDEX: false, BUILDER: 'html', FILE_SUFFIX: '.html', diff --git a/docs/build/html/_static/js/versions.js b/docs/build/html/_static/js/versions.js index 4958195..6e533ff 100644 --- a/docs/build/html/_static/js/versions.js +++ b/docs/build/html/_static/js/versions.js @@ -105,7 +105,7 @@ if (themeFlyoutDisplay === "attached") {
-
Search
+
검색
Sphinx %(sphinx_version)s.": "Sphinx %(sphinx_version)s \ubc84\uc804\uc73c\ub85c \uc0dd\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4.", + "Expand sidebar": "\uc0ac\uc774\ub4dc\ubc14 \uc5f4\uae30", + "Full index on one page": "\ud55c \ud398\uc774\uc9c0\uc5d0 \uc804\uccb4 \uc0c9\uc778 \ubcf4\uae30", + "General Index": "\uc804\uccb4 \uc0c9\uc778", + "Global Module Index": "\ubaa8\ub4c8 \ucd1d \uc0c9\uc778", + "Go": "\uc774\ub3d9", + "Hide Search Matches": "\uac80\uc0c9 \uc77c\uce58 \uc228\uae30\uae30", + "Index": "\uc0c9\uc778", + "Index – %(key)s": "", + "Index pages by letter": "\uc54c\ud30c\ubcb3\ubcc4 \uc0c9\uc778", + "Indices and tables:": "\uc0c9\uc778 \ubc0f \ud45c \ubaa9\ub85d:", + "Last updated on %(last_updated)s.": "\ucd5c\uc885 \uc5c5\ub370\uc774\ud2b8: %(last_updated)s", + "Library changes": "\ub77c\uc774\ube0c\ub7ec\ub9ac \ubcc0\uacbd \uc0ac\ud56d", + "Navigation": "\ud0d0\uc0c9", + "Next topic": "\ub2e4\uc74c \ud56d\ubaa9", + "Other changes": "\ub2e4\ub978 \ubcc0\uacbd \uc0ac\ud56d", + "Overview": "\uac1c\uc694", + "Please activate JavaScript to enable the search\n functionality.": "\uac80\uc0c9 \uae30\ub2a5\uc744 \uc0ac\uc6a9\ud558\ub824\uba74 JavaScript\ub97c \ud65c\uc131\ud654\ud558\uc2ed\uc2dc\uc624.", + "Preparing search...": "\uac80\uc0c9 \uc900\ube44 \uc911\u2026", + "Previous topic": "\uc774\uc804 \ud56d\ubaa9", + "Quick search": "\ube60\ub978 \uac80\uc0c9", + "Search": "\uac80\uc0c9", + "Search Page": "\uac80\uc0c9 \ud398\uc774\uc9c0", + "Search Results": "\uac80\uc0c9 \uacb0\uacfc", + "Search finished, found one page matching the search query.": [ + "" + ], + "Search within %(docstitle)s": "%(docstitle)s\uc5d0\uc11c \ucc3e\uae30", + "Searching": "\uac80\uc0c9 \uc911", + "Searching for multiple words only shows matches that contain\n all words.": "\uc5ec\ub7ec \ub2e8\uc5b4\ub97c \uac80\uc0c9\ud558\uba74 \ubaa8\ub4e0 \ub2e8\uc5b4\uac00 \ud3ec\ud568\ub41c \uc77c\uce58 \ud56d\ubaa9\ub9cc \ud45c\uc2dc\ub429\ub2c8\ub2e4.", + "Show Source": "\uc18c\uc2a4 \ubcf4\uae30", + "Table of Contents": "\ubaa9\ucc28", + "This Page": "\ud604\uc7ac \ubb38\uc11c", + "Welcome! This is": "\ud658\uc601\ud569\ub2c8\ub2e4!", + "Your search did not match any documents. Please make sure that all words are spelled correctly and that you've selected enough categories.": "\uac80\uc0c9\uc5b4\uc640 \uc77c\uce58\ud558\ub294 \ubb38\uc11c\uac00 \uc5c6\uc2b5\ub2c8\ub2e4. \ubaa8\ub4e0 \ub2e8\uc5b4\uc758 \ucca0\uc790\uac00 \uc62c\ubc14\ub978\uc9c0, \ucda9\ubd84\ud55c \uce74\ud14c\uace0\ub9ac\ub97c \uc120\ud0dd\ud588\ub294\uc9c0 \ud655\uc778\ud558\uc2ed\uc2dc\uc624.", + "all functions, classes, terms": "\ud568\uc218, \ud074\ub798\uc2a4 \ubc0f \uc6a9\uc5b4 \uac1c\uad00", + "can be huge": "\ud070 \uacbd\uc6b0\uac00 \uc788\uc73c\ubbc0\ub85c \uc8fc\uc758", + "last updated": "\ucd5c\uc885 \uc5c5\ub370\uc774\ud2b8", + "lists all sections and subsections": "\ubaa8\ub4e0 \uad6c\uc5ed\uacfc \ud558\uc704 \uad6c\uc5ed \ubaa9\ub85d", + "next chapter": "\ub2e4\uc74c \uc7a5", + "previous chapter": "\uc774\uc804 \uc7a5", + "quick access to all modules": "\ubaa8\ub4e0 \ubaa8\ub4c8 \uc870\uacac\ud45c", + "search": "\uac80\uc0c9", + "search this documentation": "\ubb38\uc11c \uac80\uc0c9", + "the documentation for": "\ubb38\uc11c:" + }, + "plural_expr": "0" +}); \ No newline at end of file diff --git a/docs/build/html/advanced_usage.html b/docs/build/html/advanced_usage.html new file mode 100644 index 0000000..ab40641 --- /dev/null +++ b/docs/build/html/advanced_usage.html @@ -0,0 +1,454 @@ + + + + + + + + + 고급 사용법 — Atio 2.0.0 문서 + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

고급 사용법

+

이 섹션에서는 Atio의 고급 기능들을 다룹니다.

+
+

스냅샷 기반 버전 관리

+

Atio는 데이터의 버전을 관리할 수 있는 스냅샷 시스템을 제공합니다.

+
+

기본 스냅샷 쓰기

+
import atio
+import pandas as pd
+
+# 데이터 생성
+df = pd.DataFrame({
+    "id": [1, 2, 3],
+    "name": ["Alice", "Bob", "Charlie"]
+})
+
+# 스냅샷으로 저장 (버전 관리)
+atio.write_snapshot(df, "users_table", format="parquet")
+
+# 새로운 데이터로 업데이트
+df_new = pd.DataFrame({
+    "id": [1, 2, 3, 4],
+    "name": ["Alice", "Bob", "Charlie", "David"]
+})
+
+# append 모드로 스냅샷 추가
+atio.write_snapshot(df_new, "users_table", mode="append", format="parquet")
+
+
+
+
+

스냅샷 읽기

+
# 최신 버전 읽기
+latest_data = atio.read_table("users_table")
+
+# 특정 버전 읽기
+version_1_data = atio.read_table("users_table", version=1)
+
+# Polars로 읽기
+polars_data = atio.read_table("users_table", output_as="polars")
+
+
+
+
+

스냅샷 정리

+
from datetime import timedelta
+
+# 7일 이상 된 스냅샷 삭제 (dry run)
+atio.expire_snapshots("users_table", keep_for=timedelta(days=7), dry_run=True)
+
+# 실제 삭제 실행
+atio.expire_snapshots("users_table", keep_for=timedelta(days=7), dry_run=False)
+
+
+
+
+
+

데이터베이스 연동

+

Pandas와 Polars를 사용하여 데이터베이스에 안전하게 데이터를 저장할 수 있습니다.

+
+

Pandas SQL 연동

+
import atio
+import pandas as pd
+from sqlalchemy import create_engine
+
+# 데이터베이스 연결
+engine = create_engine('postgresql://user:password@localhost/dbname')
+
+df = pd.DataFrame({
+    "id": [1, 2, 3],
+    "name": ["Alice", "Bob", "Charlie"]
+})
+
+# SQL 데이터베이스에 저장
+atio.write(df, format="sql", name="users", con=engine)
+
+
+
+
+

Polars 데이터베이스 연동

+
import atio
+import polars as pl
+
+df = pl.DataFrame({
+    "id": [1, 2, 3],
+    "name": ["Alice", "Bob", "Charlie"]
+})
+
+# 데이터베이스에 저장
+atio.write(df, format="database",
+           table_name="users",
+           connection_uri="postgresql://user:password@localhost/dbname")
+
+
+
+
+
+

성능 최적화

+
+

진행도 표시

+

대용량 파일 처리 시 진행 상황을 실시간으로 확인할 수 있습니다.

+
import atio
+import pandas as pd
+import numpy as np
+
+# 대용량 데이터 생성
+large_df = pd.DataFrame(np.random.randn(1000000, 10))
+
+# 진행도 표시와 함께 저장
+atio.write(large_df, "large_data.parquet",
+           format="parquet",
+           show_progress=True)
+
+
+
+
+

상세 로깅

+

성능 진단을 위한 상세한 로깅을 활성화할 수 있습니다.

+
# 상세한 성능 정보 출력
+atio.write(df, "data.parquet", format="parquet", verbose=True)
+
+
+

이를 통해 다음과 같은 정보를 확인할 수 있습니다:

+
    +
  • 각 단계별 소요 시간

  • +
  • 임시 파일 생성 및 교체 과정

  • +
  • 백업 및 롤백 과정

  • +
  • 성능 병목점 분석

  • +
+
+
+
+

에러 처리

+

Atio는 다양한 에러 상황에 대해 안전하게 처리합니다.

+
+

파일 시스템 에러

+
import atio
+import pandas as pd
+
+df = pd.DataFrame({"a": [1, 2, 3]})
+
+try:
+    # 권한이 없는 디렉토리에 저장 시도
+    atio.write(df, "/root/data.parquet", format="parquet")
+except PermissionError as e:
+    print(f"권한 에러: {e}")
+    # 원본 파일은 그대로 보존됨
+
+
+
+
+

포맷 에러

+
try:
+    # 지원하지 않는 포맷 사용
+    atio.write(df, "data.unknown", format="unknown")
+except ValueError as e:
+    print(f"지원하지 않는 포맷: {e}")
+
+
+
+
+

데이터베이스 에러

+
try:
+    # 잘못된 데이터베이스 연결 정보
+    atio.write(df, format="sql",
+               name="users",
+               con="invalid_connection")
+except Exception as e:
+    print(f"데이터베이스 에러: {e}")
+
+
+
+
+
+

플러그인 확장

+

Atio는 플러그인 아키텍처를 통해 새로운 형식을 쉽게 추가할 수 있습니다.

+
+

커스텀 형식 등록

+
from atio.plugins import register_writer
+import pandas as pd
+
+# 커스텀 형식 등록
+def custom_writer(df, path, **kwargs):
+    # 커스텀 저장 로직
+    with open(path, 'w') as f:
+        f.write("Custom format\n")
+        f.write(df.to_string())
+
+# Pandas DataFrame에 대한 커스텀 형식 등록
+register_writer(pd.DataFrame, "custom", custom_writer)
+
+# 사용
+df = pd.DataFrame({"a": [1, 2, 3]})
+atio.write(df, "data.custom", format="custom")
+
+
+
+
+
+

NumPy 배열 처리

+

NumPy 배열의 다양한 저장 방식을 지원합니다.

+
+

단일 배열 저장

+
import atio
+import numpy as np
+
+arr = np.array([[1, 2, 3], [4, 5, 6]])
+
+# .npy 파일로 저장
+atio.write(arr, "array.npy", format="npy")
+
+# .csv 파일로 저장
+atio.write(arr, "array.csv", format="csv")
+
+
+
+
+

여러 배열 저장

+
# 여러 배열을 딕셔너리로 저장
+arrays = {
+    "features": np.random.randn(1000, 10),
+    "labels": np.random.randint(0, 2, 1000),
+    "metadata": np.array([1, 2, 3, 4, 5])
+}
+
+# 압축된 .npz 파일로 저장
+atio.write(arrays, "data.npz", format="npz_compressed")
+
+
+
+
+
+

실제 사용 사례

+
+

머신러닝 파이프라인

+
import atio
+import pandas as pd
+from sklearn.model_selection import train_test_split
+from sklearn.ensemble import RandomForestClassifier
+
+# 데이터 로드 및 전처리
+df = pd.read_csv("raw_data.csv")
+
+# 전처리된 데이터를 안전하게 저장
+atio.write(df, "processed_data.parquet", format="parquet")
+
+# 학습/테스트 분할
+X_train, X_test, y_train, y_test = train_test_split(
+    df.drop('target', axis=1), df['target'], test_size=0.2
+)
+
+# 분할된 데이터를 스냅샷으로 저장
+atio.write_snapshot(X_train, "training_data", format="parquet")
+atio.write_snapshot(X_test, "test_data", format="parquet")
+
+# 모델 학습
+model = RandomForestClassifier()
+model.fit(X_train, y_train)
+
+# 예측 결과를 안전하게 저장
+predictions = model.predict(X_test)
+results_df = pd.DataFrame({
+    'actual': y_test,
+    'predicted': predictions
+})
+
+atio.write(results_df, "predictions.parquet", format="parquet")
+
+
+
+
+

ETL 파이프라인

+
import atio
+import pandas as pd
+from sqlalchemy import create_engine
+
+# 1. 원본 데이터 로드
+raw_data = pd.read_csv("source_data.csv")
+
+# 2. 데이터 정제
+cleaned_data = raw_data.dropna()
+cleaned_data = cleaned_data[cleaned_data['value'] > 0]
+
+# 3. 안전하게 정제된 데이터 저장
+atio.write(cleaned_data, "cleaned_data.parquet", format="parquet")
+
+# 4. 데이터베이스에 저장
+engine = create_engine('postgresql://user:password@localhost/warehouse')
+atio.write(cleaned_data, format="sql",
+           name="processed_table",
+           con=engine,
+           if_exists='replace')
+
+# 5. 스냅샷으로 버전 관리
+atio.write_snapshot(cleaned_data, "daily_processed", format="parquet")
+
+
+
+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/build/html/api.html b/docs/build/html/api.html new file mode 100644 index 0000000..b4782d6 --- /dev/null +++ b/docs/build/html/api.html @@ -0,0 +1,489 @@ + + + + + + + + + API 참조 — Atio 2.0.0 문서 + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

API 참조

+

Atio의 모든 함수와 클래스에 대한 상세한 API 문서입니다.

+
+

주요 함수

+

Atio: 안전한 원자적 파일 쓰기 라이브러리

+
+
+

Core 모듈

+

progress 적용 후 write 함수

+
+
+atio.core.write(obj, target_path=None, format=None, show_progress=False, verbose=False, **kwargs)[소스]
+

데이터 객체(obj)를 안전하게 target_path 또는 데이터베이스에 저장합니다.

+
    +
  • 파일 기반 쓰기 (format: ‘csv’, ‘parquet’, ‘excel’ 등): +- target_path (str): 필수. 데이터가 저장될 파일 경로입니다. +- 롤백 기능이 있는 원자적 쓰기를 수행합니다.

  • +
  • 데이터베이스 기반 쓰기 (format: ‘sql’, ‘database’): +- target_path: 사용되지 않습니다. +- kwargs (dict): 데이터베이스 쓰기에 필요한 추가 인자들입니다.

    +
    +
      +
    • pandas.to_sql: ‘name’(테이블명), ‘con’(커넥션 객체)가 필수입니다.

    • +
    • polars.write_database: ‘table_name’, ‘connection_uri’가 필수입니다.

    • +
    +
    +
  • +
+
+
매개변수:
+
    +
  • obj – 저장할 데이터 객체 (e.g., pandas.DataFrame, polars.DataFrame, np.ndarray).

  • +
  • target_path (str, optional) – 파일 저장 경로. 파일 기반 쓰기 시 필수. Defaults to None.

  • +
  • format (str, optional) – 저장할 포맷. Defaults to None.

  • +
  • show_progress (bool) – 진행도 표시 여부. Defaults to False.

  • +
  • verbose (bool) – 상세한 성능 진단 정보 출력 여부. Defaults to False.

  • +
  • **kwargs – 각 쓰기 함수에 전달될 추가 키워드 인자.

  • +
+
+
+
+ +
+
+atio.core.write_snapshot(obj, table_path, mode='overwrite', format='parquet', **kwargs)[소스]
+
+ +
+
+atio.core.read_table(table_path, version=None, output_as='pandas')[소스]
+
+ +
+
+atio.core.expire_snapshots(table_path, keep_for=datetime.timedelta(days=7), dry_run=True)[소스]
+

설정된 보관 기간(keep_for)보다 오래된 스냅샷과 +더 이상 참조되지 않는 데이터 파일을 삭제합니다.

+
+ +
+
+

write()

+

안전한 원자적 파일 쓰기를 수행하는 메인 함수입니다.

+
+
+atio.write(obj, target_path=None, format='parquet', **kwargs)
+
+
매개변수:
+
    +
  • obj – 저장할 데이터 객체 (pandas.DataFrame, polars.DataFrame, numpy.ndarray)

  • +
  • target_path – 저장할 파일 경로 (파일 저장 시 필수)

  • +
  • format – 저장 형식 (‘csv’, ‘parquet’, ‘excel’, ‘json’, ‘sql’, ‘database’)

  • +
  • show_progress – 진행률 표시 여부 (기본값: False)

  • +
  • verbose – 상세 성능 정보 출력 여부 (기본값: False)

  • +
  • **kwargs

    형식별 추가 매개변수

    +

  • +
+
+
반환:
+

None

+
+
Raises:
+

ValueError, IOError, DatabaseError

+
+
+

사용 예제:

+
import atio
+import pandas as pd
+
+df = pd.DataFrame({"a": [1, 2, 3]})
+
+# 기본 사용법
+atio.write(df, "data.parquet", format="parquet")
+
+# 진행률 표시
+atio.write(df, "data.parquet", format="parquet", show_progress=True)
+
+# 성능 정보 출력
+atio.write(df, "data.parquet", format="parquet", verbose=True)
+
+
+
+ +
+
+

write_snapshot()

+

데이터 스냅샷을 생성하여 버전 관리를 수행합니다.

+
+
+atio.write_snapshot(obj, table_name, format='parquet', **kwargs)
+
+
매개변수:
+
    +
  • obj – 저장할 데이터 객체

  • +
  • table_name – 테이블 이름 (스냅샷 디렉토리명)

  • +
  • format – 저장 형식

  • +
  • **kwargs

    추가 매개변수

    +

  • +
+
+
반환:
+

생성된 스냅샷 ID

+
+
+

사용 예제:

+
# 스냅샷 생성
+snapshot_id = atio.write_snapshot(df, "users", format="parquet")
+print(f"생성된 스냅샷 ID: {snapshot_id}")
+
+
+
+ +
+
+

read_table()

+

스냅샷에서 데이터를 읽어옵니다.

+
+
+atio.read_table(table_name, snapshot_id='latest', format='parquet', **kwargs)
+
+
매개변수:
+
    +
  • table_name – 테이블 이름

  • +
  • snapshot_id – 스냅샷 ID (기본값: ‘latest’)

  • +
  • format – 읽을 형식

  • +
  • **kwargs

    추가 매개변수

    +

  • +
+
+
반환:
+

데이터 객체

+
+
+

사용 예제:

+
# 최신 스냅샷 읽기
+df = atio.read_table("users", format="parquet")
+
+# 특정 스냅샷 읽기
+df = atio.read_table("users", snapshot_id="20240101_120000", format="parquet")
+
+
+
+ +
+
+

expire_snapshots()

+

오래된 스냅샷을 정리합니다.

+
+
+atio.expire_snapshots(table_name, days=30, format='parquet')
+
+
매개변수:
+
    +
  • table_name – 테이블 이름

  • +
  • days – 보관할 일수 (기본값: 30)

  • +
  • format – 형식

  • +
+
+
반환:
+

삭제된 스냅샷 수

+
+
+

사용 예제:

+
# 30일 이상 된 스냅샷 정리
+deleted_count = atio.expire_snapshots("users", days=30)
+print(f"삭제된 스냅샷 수: {deleted_count}")
+
+
+
+ +
+
+

Plugins 모듈

+
+
+atio.plugins.register_writer(obj_type, fmt, handler)[소스]
+

(객체 타입, 포맷) 쌍으로 쓰기 핸들러를 등록

+
+ +
+
+atio.plugins.get_writer(obj, fmt)[소스]
+

객체의 타입과 포맷에 맞는 핸들러를 조회

+
+ +
+
+

Utils 모듈

+
+
+atio.utils.setup_logger(name='atio', debug_level=False)[소스]
+
+ +
+
+atio.utils.check_file_exists(path)[소스]
+
+ +
+
+class atio.utils.ProgressBar(filepath: str, stop_event: Event, description: str = 'Writing')[소스]
+

기반 클래스: object

+

파일 쓰기 진행 상황을 콘솔에 표시하는 클래스. +스피너, 처리된 용량, 처리 속도, 경과 시간을 표시합니다.

+
+
+__init__(filepath: str, stop_event: Event, description: str = 'Writing')[소스]
+
+ +
+
+run()[소스]
+

진행도 막대를 실행하는 메인 루프. +이 함수가 모니터링 스레드에서 실행됩니다.

+
+ +
+ +
+
+atio.utils.read_json(path: str)[소스]
+
+ +
+
+atio.utils.write_json(data: dict, path: str)[소스]
+
+ +
+
+

지원하는 형식

+
+

CSV 형식

+
atio.write(df, "data.csv", format="csv", index=False, encoding='utf-8')
+
+
+

지원 매개변수: +- index: 인덱스 포함 여부 +- encoding: 인코딩 방식 +- sep: 구분자 +- na_rep: NA 값 표현

+
+
+

Parquet 형식

+
atio.write(df, "data.parquet", format="parquet", compression='snappy')
+
+
+

지원 매개변수: +- compression: 압축 방식 (‘snappy’, ‘gzip’, ‘brotli’) +- engine: 엔진 (‘pyarrow’, ‘fastparquet’)

+
+
+

Excel 형식

+
atio.write(df, "data.xlsx", format="excel", sheet_name="Sheet1")
+
+
+

지원 매개변수: +- sheet_name: 시트 이름 +- engine: 엔진 (‘openpyxl’, ‘xlsxwriter’)

+
+
+

JSON 형식

+
atio.write(df, "data.json", format="json", orient="records")
+
+
+

지원 매개변수: +- orient: 방향 (‘records’, ‘split’, ‘index’, ‘columns’, ‘values’, ‘table’)

+
+
+

SQL 형식

+
atio.write(df, format="sql", name="table_name", con=engine, if_exists="replace")
+
+
+

지원 매개변수: +- name: 테이블 이름 +- con: 데이터베이스 연결 +- if_exists: 테이블 존재 시 동작 (‘fail’, ‘replace’, ‘append’)

+
+
+
+

예외 처리

+

Atio는 다음과 같은 예외를 발생시킬 수 있습니다:

+
    +
  • ValueError: 잘못된 매개변수나 형식

  • +
  • IOError: 파일 시스템 오류

  • +
  • DatabaseError: 데이터베이스 연결 오류

  • +
  • ImportError: 필요한 패키지가 설치되지 않은 경우

  • +
+

예외 처리 예제:

+
try:
+    atio.write(df, "data.parquet", format="parquet")
+except ValueError as e:
+    print(f"매개변수 오류: {e}")
+except IOError as e:
+    print(f"파일 시스템 오류: {e}")
+except Exception as e:
+    print(f"예상치 못한 오류: {e}")
+
+
+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/build/html/api/_autosummary/atio.core.html b/docs/build/html/api/_autosummary/atio.core.html deleted file mode 100644 index 092c679..0000000 --- a/docs/build/html/api/_autosummary/atio.core.html +++ /dev/null @@ -1,128 +0,0 @@ - - - - - - - - - atio.core — atio documentation - - - - - - - - - - - - - - - - - -
- - -
- -
-
-
- -
-
-
-
- -
-

atio.core

-

progress 적용 후 write 함수

-

Functions

- - - - - - -

write(obj, target_path[, format, ...])

데이터 객체(obj)를 안전하게 target_path에 저장합니다.

-
- - -
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/docs/build/html/api/_autosummary/atio.html b/docs/build/html/api/_autosummary/atio.html deleted file mode 100644 index 665833e..0000000 --- a/docs/build/html/api/_autosummary/atio.html +++ /dev/null @@ -1,133 +0,0 @@ - - - - - - - - - atio — atio documentation - - - - - - - - - - - - - - - - - -
- - -
- -
-
-
- -
-
-
-
- -
-

atio

-

Atio: 안전한 원자적 파일 쓰기 라이브러리

-

Modules

- - - - - - - - - - - - -

core

progress 적용 후 write 함수

plugins

utils

-
- - -
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/docs/build/html/api/_autosummary/atio.plugins.html b/docs/build/html/api/_autosummary/atio.plugins.html deleted file mode 100644 index 8173458..0000000 --- a/docs/build/html/api/_autosummary/atio.plugins.html +++ /dev/null @@ -1,130 +0,0 @@ - - - - - - - - - atio.plugins — atio documentation - - - - - - - - - - - - - - - - - -
- - -
- -
-
-
- -
-
-
-
- -
-

atio.plugins

-

Functions

- - - - - - - - - -

get_writer(obj, fmt)

객체의 타입과 포맷에 맞는 핸들러를 조회

register_writer(obj_type, fmt, handler)

(객체 타입, 포맷) 쌍으로 쓰기 핸들러를 등록

-
- - -
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/docs/build/html/api/_autosummary/atio.utils.html b/docs/build/html/api/_autosummary/atio.utils.html deleted file mode 100644 index 63844a1..0000000 --- a/docs/build/html/api/_autosummary/atio.utils.html +++ /dev/null @@ -1,136 +0,0 @@ - - - - - - - - - atio.utils — atio documentation - - - - - - - - - - - - - - - - -
- - -
- -
-
-
- -
-
-
-
- -
-

atio.utils

-

Functions

- - - - - - - - - -

check_file_exists(path)

setup_logger([name, debug_level])

-

Classes

- - - - - - -

ProgressBar(filepath, stop_event[, description])

파일 쓰기 진행 상황을 콘솔에 표시하는 클래스.

-
- - -
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/docs/build/html/configuration.html b/docs/build/html/configuration.html new file mode 100644 index 0000000..6cb5489 --- /dev/null +++ b/docs/build/html/configuration.html @@ -0,0 +1,629 @@ + + + + + + + + + 설정 및 구성 — Atio 2.0.0 문서 + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

설정 및 구성

+

Atio의 다양한 설정 옵션과 구성 방법을 설명합니다.

+
+

로깅 설정

+

Atio는 상세한 로깅을 통해 작업 과정을 추적할 수 있습니다.

+
+

기본 로깅

+
import atio
+import pandas as pd
+
+df = pd.DataFrame({"a": [1, 2, 3]})
+
+# 기본 로깅 (INFO 레벨)
+atio.write(df, "data.parquet", format="parquet")
+
+
+

출력 예시: +` +[INFO] 임시 디렉토리 생성: /tmp/tmp12345 +[INFO] 임시 파일 경로: /tmp/tmp12345/data.parquet +[INFO] 사용할 writer: to_parquet (format: parquet) +[INFO] 데이터 임시 파일에 저장 완료: /tmp/tmp12345/data.parquet +[INFO] 원자적 교체 완료: /tmp/tmp12345/data.parquet -> data.parquet +[INFO] _SUCCESS 플래그 파일 생성: .data.parquet._SUCCESS +[INFO] Atomic write completed successfully (took 0.1234s) +`

+
+
+

상세 로깅

+
# 상세한 성능 정보 출력 (DEBUG 레벨)
+atio.write(df, "data.parquet", format="parquet", verbose=True)
+
+
+

출력 예시: +` +[INFO] 임시 디렉토리 생성: /tmp/tmp12345 +[INFO] 임시 파일 경로: /tmp/tmp12345/data.parquet +[INFO] 사용할 writer: to_parquet (format: parquet) +[INFO] 데이터 임시 파일에 저장 완료: /tmp/tmp12345/data.parquet +[INFO] 원자적 교체 완료: /tmp/tmp12345/data.parquet -> data.parquet +[INFO] _SUCCESS 플래그 파일 생성: .data.parquet._SUCCESS +[DEBUG] Atomic write step timings (SUCCESS): setup=0.0012s, write_call=0.0987s, replace=0.0001s, success_flag=0.0001s, total=0.1001s +[INFO] Atomic write completed successfully (took 0.1001s) +`

+
+
+
+

진행도 표시

+

대용량 파일 처리 시 진행 상황을 실시간으로 확인할 수 있습니다.

+
+

기본 진행도 표시

+
import atio
+import pandas as pd
+import numpy as np
+
+# 대용량 데이터 생성
+large_df = pd.DataFrame(np.random.randn(1000000, 10))
+
+# 진행도 표시와 함께 저장
+atio.write(large_df, "large_data.parquet",
+           format="parquet",
+           show_progress=True)
+
+
+

출력 예시: +` + Writing large_data.parquet... [ 45.2 MB | 12.3 MB/s | 00:03 ] + Writing large_data.parquet... [ 67.8 MB | 11.9 MB/s | 00:05 ] + Writing large_data.parquet... [ 89.1 MB | 12.1 MB/s | 00:07 ] + Writing completed successfully (89.1 MB in 7s) +`

+
+
+

진행도 표시 옵션

+

진행도 표시는 다음과 같은 정보를 제공합니다:

+
    +
  • 스피너: 작업 진행 상태를 시각적으로 표시

  • +
  • 파일 크기: 현재까지 저장된 데이터 크기

  • +
  • 처리 속도: 초당 처리되는 데이터 양

  • +
  • 경과 시간: 작업 시작 후 경과한 시간

  • +
+
+
+
+

성능 최적화 설정

+
+

메모리 사용량 최적화

+
# 대용량 데이터 처리 시 메모리 효율적인 설정
+atio.write(large_df, "data.parquet", format="parquet",
+           compression='snappy',  # 빠른 압축
+           index=False)          # 인덱스 제외로 메모리 절약
+
+
+
+
+

압축 설정

+
# 속도 우선 (압축 없음)
+atio.write(df, "data.parquet", format="parquet", compression=None)
+
+# 균형 (snappy 압축)
+atio.write(df, "data.parquet", format="parquet", compression='snappy')
+
+# 용량 우선 (gzip 압축)
+atio.write(df, "data.parquet", format="parquet", compression='gzip')
+
+
+
+
+
+

임시 디렉토리 설정

+

기본적으로 Atio는 시스템의 임시 디렉토리를 사용합니다.

+
+

사용자 정의 임시 디렉토리

+
import os
+import tempfile
+
+# 임시 디렉토리 설정
+tempfile.tempdir = "/path/to/custom/temp"
+
+# 또는 환경 변수 설정
+os.environ['TMPDIR'] = "/path/to/custom/temp"
+
+
+

주의사항: +- 임시 디렉토리는 충분한 디스크 공간이 있어야 합니다 +- 쓰기 권한이 있어야 합니다 +- 빠른 I/O 성능을 위해 SSD를 권장합니다

+
+
+
+

에러 처리 설정

+

Atio는 다양한 에러 상황에 대해 안전하게 처리합니다.

+
+

롤백 동작

+
# 기본적으로 롤백이 자동으로 수행됩니다
+try:
+    atio.write(df, "data.parquet", format="parquet")
+except Exception as e:
+    # 에러 발생 시 원본 파일은 보존됩니다
+    print(f"저장 실패: {e}")
+    # 임시 파일은 자동으로 정리됩니다
+
+
+
+
+

백업 파일 관리

+
# 백업 파일은 작업 성공 시 자동으로 삭제됩니다
+# 실패 시에는 롤백 후 삭제됩니다
+
+# 백업 파일이 남아있는 경우 수동으로 확인
+import os
+backup_file = "data.parquet._backup"
+if os.path.exists(backup_file):
+    print("백업 파일이 존재합니다. 수동 확인이 필요할 수 있습니다.")
+
+
+
+
+
+

완료 플래그 시스템

+

Atio는 작업 완료를 확인할 수 있는 플래그 파일을 생성합니다.

+
+

플래그 파일 확인

+
import os
+
+# 저장 완료 후 플래그 파일 확인
+atio.write(df, "data.parquet", format="parquet")
+
+# 플래그 파일 경로
+flag_file = ".data.parquet._SUCCESS"
+
+if os.path.exists(flag_file):
+    print("저장이 성공적으로 완료되었습니다.")
+else:
+    print("저장이 완료되지 않았거나 실패했습니다.")
+
+
+
+
+

플래그 파일 활용

+
# 배치 처리에서 완료 여부 확인
+files_to_process = ["data1.parquet", "data2.parquet", "data3.parquet"]
+
+for file in files_to_process:
+    flag_file = f".{file}._SUCCESS"
+    if not os.path.exists(flag_file):
+        print(f"{file} 처리가 완료되지 않았습니다.")
+        # 재처리 로직
+
+
+
+
+
+

스냅샷 설정

+

스냅샷 시스템의 다양한 설정 옵션을 설명합니다.

+
+

스냅샷 모드

+
# overwrite 모드 (기본값)
+atio.write_snapshot(df, "table_path", mode="overwrite", format="parquet")
+
+# append 모드 (기존 데이터에 추가)
+atio.write_snapshot(df, "table_path", mode="append", format="parquet")
+
+
+
+
+

스냅샷 정리 설정

+
from datetime import timedelta
+
+# 7일 이상 된 스냅샷 삭제
+atio.expire_snapshots("table_path",
+                     keep_for=timedelta(days=7),
+                     dry_run=True)  # 실제 삭제 전 확인
+
+# 30일 이상 된 스냅샷 삭제
+atio.expire_snapshots("table_path",
+                     keep_for=timedelta(days=30),
+                     dry_run=False)  # 실제 삭제
+
+
+
+
+
+

환경 변수 설정

+

Atio의 동작을 제어하는 환경 변수들을 설정할 수 있습니다.

+
+

로깅 레벨 설정

+
import os
+
+# DEBUG 레벨로 로깅 설정
+os.environ['ATIO_LOG_LEVEL'] = 'DEBUG'
+
+# INFO 레벨로 로깅 설정 (기본값)
+os.environ['ATIO_LOG_LEVEL'] = 'INFO'
+
+
+
+
+

임시 디렉토리 설정

+
# 임시 디렉토리 경로 설정
+os.environ['ATIO_TEMP_DIR'] = '/path/to/temp'
+
+# 또는 시스템 임시 디렉토리 설정
+os.environ['TMPDIR'] = '/path/to/temp'
+
+
+
+
+

성능 모니터링 설정

+
# 성능 모니터링 활성화
+os.environ['ATIO_PERFORMANCE_MONITORING'] = 'true'
+
+# 성능 모니터링 비활성화
+os.environ['ATIO_PERFORMANCE_MONITORING'] = 'false'
+
+
+
+
+
+

플러그인 설정

+

커스텀 플러그인을 등록하여 새로운 형식을 지원할 수 있습니다.

+
+

플러그인 등록

+
from atio.plugins import register_writer
+import pandas as pd
+
+# 커스텀 형식 등록
+def custom_writer(df, path, **kwargs):
+    # 커스텀 저장 로직
+    with open(path, 'w') as f:
+        f.write("Custom format\n")
+        f.write(df.to_string())
+
+# 등록
+register_writer(pd.DataFrame, "custom", custom_writer)
+
+
+
+
+

플러그인 확인

+
from atio.plugins import WRITER_MAPPING
+
+# 등록된 플러그인 확인
+for obj_type, formats in WRITER_MAPPING.items():
+    print(f"Object type: {obj_type.__name__}")
+    for fmt, handler in formats.items():
+        print(f"  - {fmt}: {handler}")
+
+
+
+
+
+

설정 파일 사용

+

설정을 파일로 관리하여 일관된 설정을 유지할 수 있습니다.

+
+

JSON 설정 파일

+
import json
+
+# 설정 파일 생성
+config = {
+    "default_format": "parquet",
+    "compression": "snappy",
+    "show_progress": True,
+    "verbose": False,
+    "temp_dir": "/path/to/temp"
+}
+
+with open("atio_config.json", "w") as f:
+    json.dump(config, f, indent=2)
+
+# 설정 파일 읽기
+with open("atio_config.json", "r") as f:
+    config = json.load(f)
+
+# 설정 적용
+atio.write(df, "data.parquet",
+           format=config.get("default_format", "parquet"),
+           compression=config.get("compression", "snappy"),
+           show_progress=config.get("show_progress", False),
+           verbose=config.get("verbose", False))
+
+
+
+
+

YAML 설정 파일

+
import yaml
+
+# 설정 파일 생성
+config = {
+    "default_format": "parquet",
+    "compression": "snappy",
+    "show_progress": True,
+    "verbose": False,
+    "temp_dir": "/path/to/temp"
+}
+
+with open("atio_config.yaml", "w") as f:
+    yaml.dump(config, f)
+
+# 설정 파일 읽기
+with open("atio_config.yaml", "r") as f:
+    config = yaml.safe_load(f)
+
+
+
+
+
+

모범 사례

+
+

프로덕션 환경 설정

+
# 프로덕션 환경을 위한 설정
+import os
+import tempfile
+
+# 1. 전용 임시 디렉토리 설정
+tempfile.tempdir = "/var/tmp/atio"
+os.makedirs(tempfile.tempdir, exist_ok=True)
+
+# 2. 로깅 레벨 설정
+os.environ['ATIO_LOG_LEVEL'] = 'INFO'
+
+# 3. 성능 최적화 설정
+def safe_write(df, path, **kwargs):
+    return atio.write(df, path,
+                     format="parquet",
+                     compression="snappy",
+                     show_progress=True,
+                     verbose=False,
+                     **kwargs)
+
+
+
+
+

개발 환경 설정

+
# 개발 환경을 위한 설정
+import os
+
+# 1. 상세 로깅 활성화
+os.environ['ATIO_LOG_LEVEL'] = 'DEBUG'
+
+# 2. 성능 모니터링 활성화
+os.environ['ATIO_PERFORMANCE_MONITORING'] = 'true'
+
+# 3. 개발용 설정
+def dev_write(df, path, **kwargs):
+    return atio.write(df, path,
+                     format="parquet",
+                     compression=None,  # 압축 없음으로 빠른 처리
+                     show_progress=True,
+                     verbose=True,  # 상세 정보 출력
+                     **kwargs)
+
+
+
+
+
+

설정 검증

+

설정이 올바르게 적용되었는지 확인하는 방법을 설명합니다.

+
+

기본 검증

+
import atio
+import pandas as pd
+import tempfile
+
+# 테스트 데이터 생성
+df = pd.DataFrame({"test": [1, 2, 3]})
+
+# 설정 테스트
+def test_config():
+    # 임시 디렉토리 확인
+    print(f"임시 디렉토리: {tempfile.gettempdir()}")
+
+    # 로깅 테스트
+    atio.write(df, "test.parquet", format="parquet", verbose=True)
+
+    # 플래그 파일 확인
+    import os
+    if os.path.exists(".test.parquet._SUCCESS"):
+        print("설정이 올바르게 작동합니다.")
+    else:
+        print("설정에 문제가 있을 수 있습니다.")
+
+
+
+
+

성능 테스트

+
import time
+import pandas as pd
+import numpy as np
+
+# 성능 테스트
+def performance_test():
+    # 대용량 데이터 생성
+    large_df = pd.DataFrame(np.random.randn(100000, 10))
+
+    # 성능 측정
+    start_time = time.time()
+    atio.write(large_df, "performance_test.parquet",
+               format="parquet",
+               show_progress=True)
+    end_time = time.time()
+
+    print(f"처리 시간: {end_time - start_time:.2f}초")
+
+    # 파일 크기 확인
+    import os
+    file_size = os.path.getsize("performance_test.parquet")
+    print(f"파일 크기: {file_size / 1024 / 1024:.2f} MB")
+
+
+
+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/build/html/examples.html b/docs/build/html/examples.html new file mode 100644 index 0000000..75ea738 --- /dev/null +++ b/docs/build/html/examples.html @@ -0,0 +1,539 @@ + + + + + + + + + 사용 예제 — Atio 2.0.0 문서 + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

사용 예제

+

Atio의 다양한 사용 사례와 고급 기능을 살펴보세요.

+
+

기본 예제

+
+

간단한 데이터 저장

+
import atio
+import pandas as pd
+
+# 샘플 데이터 생성
+data = {
+    "id": [1, 2, 3, 4, 5],
+    "name": ["Alice", "Bob", "Charlie", "Diana", "Eve"],
+    "age": [25, 30, 35, 28, 32],
+    "city": ["Seoul", "Busan", "Incheon", "Daegu", "Daejeon"],
+    "salary": [50000, 60000, 70000, 55000, 65000]
+}
+
+df = pd.DataFrame(data)
+
+# 다양한 형식으로 저장
+atio.write(df, "employees.parquet", format="parquet")
+atio.write(df, "employees.csv", format="csv", index=False)
+atio.write(df, "employees.xlsx", format="excel", sheet_name="Employees")
+
+
+
+
+
+

대용량 데이터 처리

+
+

진행률 표시와 성능 모니터링

+
import numpy as np
+import pandas as pd
+import atio
+
+# 대용량 데이터 생성 (100만 행)
+large_data = {
+    "id": range(1, 1000001),
+    "value": np.random.randn(1000000),
+    "category": np.random.choice(["A", "B", "C"], 1000000),
+    "timestamp": pd.date_range("2024-01-01", periods=1000000, freq="S")
+}
+
+large_df = pd.DataFrame(large_data)
+
+# 진행률 표시와 성능 모니터링 활성화
+atio.write(
+    large_df,
+    "large_dataset.parquet",
+    format="parquet",
+    show_progress=True,
+    verbose=True,
+    compression='snappy'
+)
+
+
+
+
+
+

데이터베이스 연동

+
+

PostgreSQL 데이터베이스 저장

+
import atio
+import pandas as pd
+from sqlalchemy import create_engine
+
+# 데이터베이스 연결
+engine = create_engine('postgresql://username:password@localhost:5432/mydb')
+
+# 샘플 데이터
+sales_data = {
+    "order_id": [1001, 1002, 1003, 1004, 1005],
+    "product_name": ["Laptop", "Mouse", "Keyboard", "Monitor", "Headphones"],
+    "quantity": [1, 2, 1, 1, 3],
+    "price": [1200, 25, 75, 300, 150],
+    "order_date": pd.date_range("2024-01-01", periods=5)
+}
+
+sales_df = pd.DataFrame(sales_data)
+
+# 데이터베이스에 안전하게 저장
+atio.write(
+    sales_df,
+    format="sql",
+    name="sales_orders",
+    con=engine,
+    if_exists="replace",
+    index=False
+)
+
+
+
+
+
+

스냅샷 기반 버전 관리

+
+

데이터 버전 관리

+
import atio
+import pandas as pd
+from datetime import datetime, timedelta
+
+# 초기 데이터
+initial_data = {
+    "user_id": [1, 2, 3],
+    "name": ["Alice", "Bob", "Charlie"],
+    "status": ["active", "active", "inactive"]
+}
+
+df = pd.DataFrame(initial_data)
+
+# 초기 스냅샷 생성
+snapshot_id_1 = atio.write_snapshot(df, "users", format="parquet")
+print(f"초기 스냅샷 생성: {snapshot_id_1}")
+
+# 데이터 업데이트
+df.loc[df['user_id'] == 3, 'status'] = 'active'
+df = df.append({"user_id": 4, "name": "Diana", "status": "active"}, ignore_index=True)
+
+# 업데이트된 스냅샷 생성
+snapshot_id_2 = atio.write_snapshot(df, "users", format="parquet")
+print(f"업데이트 스냅샷 생성: {snapshot_id_2}")
+
+# 최신 데이터 읽기
+latest_df = atio.read_table("users", format="parquet")
+print("최신 데이터:")
+print(latest_df)
+
+# 특정 스냅샷 읽기
+initial_df = atio.read_table("users", snapshot_id=snapshot_id_1, format="parquet")
+print("초기 데이터:")
+print(initial_df)
+
+# 오래된 스냅샷 정리 (7일 이상)
+deleted_count = atio.expire_snapshots("users", days=7, format="parquet")
+print(f"삭제된 스냅샷 수: {deleted_count}")
+
+
+
+
+
+

Polars DataFrame 활용

+
+

고성능 데이터 처리

+
import atio
+import polars as pl
+import numpy as np
+
+# Polars DataFrame 생성
+polars_data = {
+    "id": range(1, 10001),
+    "value": np.random.randn(10000),
+    "category": np.random.choice(["A", "B", "C", "D"], 10000),
+    "score": np.random.uniform(0, 100, 10000)
+}
+
+polars_df = pl.DataFrame(polars_data)
+
+# Polars DataFrame 저장
+atio.write(
+    polars_df,
+    "polars_data.parquet",
+    format="parquet",
+    compression='snappy',
+    show_progress=True
+)
+
+# 데이터 변환 후 저장
+filtered_df = polars_df.filter(pl.col("score") > 50)
+aggregated_df = filtered_df.group_by("category").agg(
+    pl.col("value").mean().alias("avg_value"),
+    pl.col("score").mean().alias("avg_score")
+)
+
+atio.write(aggregated_df, "aggregated_data.parquet", format="parquet")
+
+
+
+
+
+

에러 처리 및 복구

+
+

안전한 데이터 처리

+
import atio
+import pandas as pd
+import os
+
+def safe_data_processing():
+    try:
+        # 원본 파일이 있는지 확인
+        if os.path.exists("important_data.parquet"):
+            print("원본 파일이 존재합니다.")
+
+        # 데이터 처리 및 저장
+        df = pd.DataFrame({
+            "id": [1, 2, 3],
+            "data": ["important", "data", "here"]
+        })
+
+        atio.write(df, "important_data.parquet", format="parquet")
+        print("데이터가 안전하게 저장되었습니다.")
+
+        # SUCCESS 파일 확인
+        if os.path.exists("important_data.parquet_SUCCESS"):
+            print("저장 완료 플래그가 생성되었습니다.")
+
+    except Exception as e:
+        print(f"오류 발생: {e}")
+        print("원본 파일이 보존되었습니다.")
+
+        # 임시 파일 정리
+        temp_files = [f for f in os.listdir(".") if f.startswith("important_data.parquet.tmp")]
+        for temp_file in temp_files:
+            try:
+                os.remove(temp_file)
+                print(f"임시 파일 정리: {temp_file}")
+            except:
+                pass
+
+# 안전한 데이터 처리 실행
+safe_data_processing()
+
+
+
+
+
+

배치 처리

+
+

여러 파일 동시 처리

+
import atio
+import pandas as pd
+import os
+from pathlib import Path
+
+def process_multiple_files():
+    # 처리할 파일 목록
+    files_to_process = [
+        {"name": "users", "data": pd.DataFrame({"id": [1, 2], "name": ["Alice", "Bob"]})},
+        {"name": "products", "data": pd.DataFrame({"id": [1, 2], "product": ["Laptop", "Mouse"]})},
+        {"name": "orders", "data": pd.DataFrame({"id": [1, 2], "amount": [100, 200]})}
+    ]
+
+    # 각 파일을 안전하게 처리
+    for file_info in files_to_process:
+        try:
+            file_path = f"{file_info['name']}.parquet"
+            atio.write(
+                file_info['data'],
+                file_path,
+                format="parquet",
+                show_progress=True
+            )
+            print(f"{file_info['name']} 파일 처리 완료")
+
+        except Exception as e:
+            print(f"{file_info['name']} 파일 처리 실패: {e}")
+            continue
+
+# 배치 처리 실행
+process_multiple_files()
+
+
+
+
+
+

성능 최적화

+
+

압축 및 최적화 설정

+
import atio
+import pandas as pd
+import numpy as np
+
+# 대용량 데이터 생성
+large_df = pd.DataFrame({
+    "id": range(1, 100001),
+    "value": np.random.randn(100000),
+    "text": ["sample text"] * 100000
+})
+
+# 다양한 압축 설정으로 성능 비교
+compression_settings = [
+    ("snappy", "fast_compression.parquet"),
+    ("gzip", "balanced_compression.parquet"),
+    ("brotli", "high_compression.parquet")
+]
+
+for compression, filename in compression_settings:
+    print(f"\n{compression} 압축으로 저장 중...")
+    atio.write(
+        large_df,
+        filename,
+        format="parquet",
+        compression=compression,
+        show_progress=True,
+        verbose=True
+    )
+
+    # 파일 크기 확인
+    file_size = os.path.getsize(filename) / (1024 * 1024)  # MB
+    print(f"파일 크기: {file_size:.2f} MB")
+
+
+
+
+
+

실제 사용 사례

+
+

데이터 파이프라인

+
import atio
+import pandas as pd
+from datetime import datetime, timedelta
+
+class DataPipeline:
+    def __init__(self, base_path="data"):
+        self.base_path = Path(base_path)
+        self.base_path.mkdir(exist_ok=True)
+
+    def extract_data(self):
+        """데이터 추출 (시뮬레이션)"""
+        # 실제로는 API나 데이터베이스에서 데이터를 가져옴
+        data = {
+            "timestamp": pd.date_range(datetime.now(), periods=1000, freq="H"),
+            "value": np.random.randn(1000),
+            "source": ["api"] * 1000
+        }
+        return pd.DataFrame(data)
+
+    def transform_data(self, df):
+        """데이터 변환"""
+        # 시간별 집계
+        df['hour'] = df['timestamp'].dt.hour
+        df['day'] = df['timestamp'].dt.date
+
+        # 일별 통계
+        daily_stats = df.groupby('day').agg({
+            'value': ['mean', 'std', 'min', 'max']
+        }).round(2)
+
+        return daily_stats
+
+    def load_data(self, df, table_name):
+        """데이터 로드"""
+        # 스냅샷 생성
+        snapshot_id = atio.write_snapshot(
+            df,
+            table_name,
+            format="parquet"
+        )
+
+        # 최신 데이터도 별도 저장
+        latest_path = self.base_path / f"{table_name}_latest.parquet"
+        atio.write(df, str(latest_path), format="parquet")
+
+        return snapshot_id
+
+    def run_pipeline(self):
+        """파이프라인 실행"""
+        print("데이터 파이프라인 시작...")
+
+        # 1. 데이터 추출
+        raw_data = self.extract_data()
+        print(f"추출된 데이터: {len(raw_data)} 행")
+
+        # 2. 데이터 변환
+        processed_data = self.transform_data(raw_data)
+        print(f"처리된 데이터: {len(processed_data)} 행")
+
+        # 3. 데이터 로드
+        snapshot_id = self.load_data(processed_data, "daily_stats")
+        print(f"스냅샷 생성 완료: {snapshot_id}")
+
+        # 4. 오래된 스냅샷 정리
+        deleted_count = atio.expire_snapshots("daily_stats", days=30)
+        print(f"정리된 스냅샷: {deleted_count}개")
+
+        print("파이프라인 완료!")
+
+# 파이프라인 실행
+pipeline = DataPipeline()
+pipeline.run_pipeline()
+
+
+
+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/build/html/formats.html b/docs/build/html/formats.html new file mode 100644 index 0000000..7d12e18 --- /dev/null +++ b/docs/build/html/formats.html @@ -0,0 +1,516 @@ + + + + + + + + + 지원하는 파일 형식 — Atio 2.0.0 문서 + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

지원하는 파일 형식

+

Atio는 다양한 데이터 형식을 지원합니다. 각 형식별로 지원하는 라이브러리와 사용법을 설명합니다.

+
+

Pandas DataFrame 형식

+

Pandas DataFrame은 가장 많은 형식을 지원합니다.

+
+

CSV (Comma-Separated Values)

+
import atio
+import pandas as pd
+
+df = pd.DataFrame({
+    "name": ["Alice", "Bob", "Charlie"],
+    "age": [25, 30, 35],
+    "city": ["Seoul", "Busan", "Incheon"]
+})
+
+# 기본 CSV 저장
+atio.write(df, "users.csv", format="csv")
+
+# 추가 옵션과 함께 저장
+atio.write(df, "users.csv", format="csv",
+           index=False,
+           encoding='utf-8',
+           sep=';')
+
+
+

지원 옵션: +- index: 인덱스 포함 여부 (기본값: True) +- encoding: 인코딩 방식 (기본값: ‘utf-8’) +- sep: 구분자 (기본값: ‘,’) +- header: 헤더 포함 여부 (기본값: True)

+
+
+

Parquet

+
# Parquet 형식으로 저장
+atio.write(df, "users.parquet", format="parquet")
+
+# 압축 옵션과 함께 저장
+atio.write(df, "users.parquet", format="parquet",
+           compression='snappy')
+
+
+

지원 옵션: +- compression: 압축 방식 (‘snappy’, ‘gzip’, ‘brotli’, None) +- index: 인덱스 포함 여부 (기본값: True)

+
+
+

Excel

+
# Excel 파일로 저장
+atio.write(df, "users.xlsx", format="excel")
+
+# 시트명과 옵션 지정
+atio.write(df, "users.xlsx", format="excel",
+           sheet_name="Users",
+           index=False)
+
+
+

지원 옵션: +- sheet_name: 시트 이름 (기본값: ‘Sheet1’) +- index: 인덱스 포함 여부 (기본값: True) +- engine: 엔진 (‘openpyxl’, ‘xlsxwriter’)

+

필요 라이브러리: pip install openpyxl

+
+
+

JSON

+
# JSON 형식으로 저장
+atio.write(df, "users.json", format="json")
+
+# 들여쓰기와 함께 저장
+atio.write(df, "users.json", format="json",
+           indent=2,
+           orient='records')
+
+
+

지원 옵션: +- orient: JSON 구조 (‘split’, ‘records’, ‘index’, ‘columns’, ‘values’, ‘table’) +- indent: 들여쓰기 크기 +- date_format: 날짜 형식

+
+
+

Pickle

+
# Pickle 형식으로 저장
+atio.write(df, "users.pkl", format="pickle")
+
+# 압축과 함께 저장
+atio.write(df, "users.pkl", format="pickle",
+           compression='gzip')
+
+
+

지원 옵션: +- compression: 압축 방식 (‘gzip’, ‘bz2’, ‘xz’, None)

+
+
+

HTML

+
# HTML 테이블로 저장
+atio.write(df, "users.html", format="html")
+
+# 스타일과 함께 저장
+atio.write(df, "users.html", format="html",
+           index=False,
+           classes='table table-striped')
+
+
+

지원 옵션: +- classes: CSS 클래스 +- index: 인덱스 포함 여부 (기본값: True)

+
+
+

SQL

+
import atio
+import pandas as pd
+from sqlalchemy import create_engine
+
+# 데이터베이스 연결
+engine = create_engine('postgresql://user:password@localhost/dbname')
+
+df = pd.DataFrame({
+    "id": [1, 2, 3],
+    "name": ["Alice", "Bob", "Charlie"]
+})
+
+# SQL 데이터베이스에 저장
+atio.write(df, format="sql",
+           name="users",
+           con=engine,
+           if_exists='replace')
+
+
+

지원 옵션: +- name: 테이블 이름 (필수) +- con: 데이터베이스 연결 객체 (필수) +- if_exists: 테이블이 존재할 때 동작 (‘fail’, ‘replace’, ‘append’) +- index: 인덱스를 컬럼으로 저장 여부 (기본값: True)

+

필요 라이브러리: pip install sqlalchemy

+
+
+
+

Polars DataFrame 형식

+

Polars는 빠른 데이터 처리를 위한 현대적인 DataFrame 라이브러리입니다.

+
+

CSV

+
import atio
+import polars as pl
+
+df = pl.DataFrame({
+    "name": ["Alice", "Bob", "Charlie"],
+    "age": [25, 30, 35],
+    "city": ["Seoul", "Busan", "Incheon"]
+})
+
+# CSV 저장
+atio.write(df, "users.csv", format="csv")
+
+# 구분자와 함께 저장
+atio.write(df, "users.csv", format="csv",
+           separator=';')
+
+
+

지원 옵션: +- separator: 구분자 (기본값: ‘,’) +- include_header: 헤더 포함 여부 (기본값: True)

+
+
+

Parquet

+
# Parquet 저장
+atio.write(df, "users.parquet", format="parquet")
+
+# 압축과 함께 저장
+atio.write(df, "users.parquet", format="parquet",
+           compression="snappy")
+
+
+

지원 옵션: +- compression: 압축 방식 (‘snappy’, ‘gzip’, ‘brotli’, ‘lz4raw’, ‘zstd’, None)

+
+
+

JSON

+
# JSON 저장
+atio.write(df, "users.json", format="json")
+
+# 파일별 저장
+atio.write(df, "users.json", format="json",
+           file=True)
+
+
+

지원 옵션: +- file: 파일별 저장 여부 (기본값: False)

+
+
+

IPC (Arrow)

+
# IPC (Arrow) 형식으로 저장
+atio.write(df, "users.arrow", format="ipc")
+
+# 압축과 함께 저장
+atio.write(df, "users.arrow", format="ipc",
+           compression="lz4")
+
+
+

지원 옵션: +- compression: 압축 방식 (‘lz4’, ‘zstd’, None)

+
+
+

Avro

+
# Avro 형식으로 저장
+atio.write(df, "users.avro", format="avro")
+
+
+

필요 라이브러리: pip install fastavro

+
+
+

Excel

+
# Excel 저장
+atio.write(df, "users.xlsx", format="excel")
+
+
+

필요 라이브러리: pip install xlsx2csv openpyxl

+
+
+

Database

+
# 데이터베이스에 저장
+atio.write(df, format="database",
+           table_name="users",
+           connection_uri="postgresql://user:password@localhost/dbname")
+
+
+

지원 옵션: +- table_name: 테이블 이름 (필수) +- connection_uri: 데이터베이스 연결 URI (필수)

+

필요 라이브러리: pip install connectorx

+
+
+
+

NumPy 배열 형식

+

NumPy 배열은 수치 데이터 처리에 최적화된 형식을 지원합니다.

+
+

NPY (NumPy Binary)

+
import atio
+import numpy as np
+
+arr = np.array([[1, 2, 3], [4, 5, 6]])
+
+# .npy 파일로 저장
+atio.write(arr, "array.npy", format="npy")
+
+
+

특징: +- 단일 배열을 효율적으로 저장 +- 메타데이터와 함께 저장 +- 빠른 읽기/쓰기 속도

+
+
+

NPZ (NumPy Compressed)

+
# 여러 배열을 딕셔너리로 저장
+arrays = {
+    "features": np.random.randn(1000, 10),
+    "labels": np.random.randint(0, 2, 1000),
+    "metadata": np.array([1, 2, 3, 4, 5])
+}
+
+# 압축되지 않은 .npz 파일로 저장
+atio.write(arrays, "data.npz", format="npz")
+
+# 압축된 .npz 파일로 저장
+atio.write(arrays, "data.npz", format="npz_compressed")
+
+
+

특징: +- 여러 배열을 하나의 파일에 저장 +- 압축 옵션으로 저장 공간 절약 +- 딕셔너리 형태로 데이터 구조화

+
+
+

CSV

+
# CSV로 저장
+atio.write(arr, "array.csv", format="csv")
+
+# 구분자와 함께 저장
+atio.write(arr, "array.csv", format="csv",
+           delimiter=';',
+           fmt='%.2f')
+
+
+

지원 옵션: +- delimiter: 구분자 (기본값: ‘,’) +- fmt: 숫자 형식 (예: ‘%.2f’, ‘%.4e’) +- header: 헤더 포함 여부 +- comments: 주석 문자

+
+
+
+

형식별 성능 비교

+

다양한 형식의 성능 특성을 비교해보겠습니다.

+
+

속도 비교

+
    +
  1. 가장 빠른 형식: +- NumPy: .npy, .npz +- Polars: .ipc (Arrow) +- Pandas: .parquet (snappy 압축)

  2. +
  3. 중간 속도: +- CSV (단순한 구조) +- JSON (중간 복잡도)

  4. +
  5. 상대적으로 느린 형식: +- Excel (복잡한 구조) +- Pickle (Python 특화)

  6. +
+
+
+

용량 비교

+
    +
  1. 가장 작은 용량: +- .parquet (컬럼 기반 압축) +- .npz_compressed (압축된 NumPy) +- .ipc (Arrow 압축)

  2. +
  3. 중간 용량: +- .npy (단일 배열) +- JSON (텍스트 기반)

  4. +
  5. 상대적으로 큰 용량: +- CSV (텍스트 기반) +- Excel (복잡한 구조)

  6. +
+
+
+

호환성 비교

+
    +
  1. 최고 호환성: +- CSV (모든 시스템에서 지원) +- JSON (웹 표준)

  2. +
  3. 좋은 호환성: +- Excel (비즈니스 환경) +- Parquet (빅데이터 생태계)

  4. +
  5. 제한적 호환성: +- .npy/.npz (Python/NumPy 특화) +- .ipc (Arrow 생태계)

  6. +
+
+
+
+

권장 사용 사례

+
+

데이터 분석 및 머신러닝

+
# 학습 데이터: 빠른 읽기/쓰기를 위해 Parquet 사용
+atio.write(training_data, "train.parquet", format="parquet")
+
+# 모델 가중치: NumPy 배열로 저장
+atio.write(model_weights, "weights.npy", format="npy")
+
+# 실험 결과: JSON으로 저장 (가독성)
+atio.write(results, "experiment_results.json", format="json")
+
+
+
+
+

웹 애플리케이션

+
# API 응답: JSON 형식
+atio.write(api_data, "response.json", format="json")
+
+# 대용량 데이터: Parquet 형식
+atio.write(large_dataset, "dataset.parquet", format="parquet")
+
+
+
+
+

데이터 파이프라인

+
# 중간 결과: 빠른 처리를 위해 IPC 사용
+atio.write(intermediate_data, "step1.arrow", format="ipc")
+
+# 최종 결과: 호환성을 위해 CSV 사용
+atio.write(final_data, "output.csv", format="csv")
+
+
+
+
+

비즈니스 보고서

+
# 엑셀 보고서
+atio.write(report_data, "monthly_report.xlsx", format="excel")
+
+# HTML 대시보드
+atio.write(dashboard_data, "dashboard.html", format="html")
+
+
+
+
+
+

형식 확장하기

+

새로운 형식을 추가하려면 플러그인 시스템을 사용하세요.

+
from atio.plugins import register_writer
+import pandas as pd
+
+# 커스텀 형식 등록
+def yaml_writer(df, path, **kwargs):
+    import yaml
+    data = df.to_dict('records')
+    with open(path, 'w') as f:
+        yaml.dump(data, f, **kwargs)
+
+# 등록
+register_writer(pd.DataFrame, "yaml", yaml_writer)
+
+# 사용
+atio.write(df, "data.yaml", format="yaml")
+
+
+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/build/html/genindex.html b/docs/build/html/genindex.html index 8459c91..5d6b9d5 100644 --- a/docs/build/html/genindex.html +++ b/docs/build/html/genindex.html @@ -1,23 +1,24 @@ - + - Index — atio documentation + 색인 — Atio 2.0.0 문서 - + + - - + + @@ -29,7 +30,7 @@ - atio + Atio
@@ -39,9 +40,17 @@
@@ -50,7 +59,7 @@
@@ -58,7 +67,7 @@
  • - +
@@ -68,13 +77,29 @@
-

Index

+

색인

- A + _ + | A + | C + | E + | G | M + | P + | R + | S + | W
+

_

+ + +
+

A

- + +
+ +

C

+ + +
+ +

E

+ + +
+ +

G

+ +
@@ -118,18 +175,64 @@

M

module +

P

+ + +
+ +

R

+ + + +
+ +

S

+ + +
+ +

W

+ + + +
+
@@ -139,7 +242,7 @@

M


-

© Copyright 2025, jujangchoi.

+

© Copyright 2025, Seo Jae Oh.

Built with Sphinx using a diff --git a/docs/build/html/index.html b/docs/build/html/index.html index 8d503f5..fd3d738 100644 --- a/docs/build/html/index.html +++ b/docs/build/html/index.html @@ -1,25 +1,26 @@ - + - Atio Documentation — atio documentation + Atio Documentation — Atio 2.0.0 문서 - + + - - - + + + @@ -31,7 +32,7 @@ - atio + Atio
@@ -41,9 +42,17 @@
@@ -52,7 +61,7 @@
@@ -62,7 +71,7 @@
  • - View page source + View page source

  • @@ -72,271 +81,343 @@

    Atio Documentation

    -
    -
    -

    Atio 🛡️

    -

    안전하고 원자적인 파일 쓰기를 지원하는 경량 Python 라이브러리입니다.
    -Pandas, Polars, NumPy 등 데이터 객체 저장 시 파일 손상 없이, 트랜잭션처럼 안전하게 처리할 수 있습니다.

    -
    -
    -

    🌟 주요 기능

    -
      -
    • ✅ 임시 디렉토리 스테이징 후 원자적 파일 교체

    • -
    • 📦 Pandas, Polars, NumPy 등 다양한 데이터 객체 지원

    • -
    • 📍 _SUCCESS 플래그 파일 생성 — 저장 완료 여부 표시

    • -
    • 🛠 실패 시 원본 파일 보존, 임시 파일 자동 정리

    • -
    • 🧩 플러그인 아키텍처로 확장성 좋음

    • -
    • 🔍 성능 진단 로깅 — 각 단계별 실행 시간 측정 및 병목점 분석

    • -
    -
    -
    -
    -

    🔍 성능 진단 로깅 (NEW!)

    -

    Atio는 이제 성능 진단 로깅 기능을 제공합니다. verbose=True 옵션을 사용하면 각 단계별 실행 시간을 측정하여 병목점을 정확히 파악할 수 있습니다.

    -
    -

    기본 사용법 (간단한 정보만):

    -
    import atio as aw
    -import pandas as pd
    -
    -df = pd.DataFrame({"a": [1, 2, 3]})
    -
    -# 기본 사용법 - 간단한 성공/실패 정보만
    -aw.write(df, "output.parquet", format="parquet")
    -
    -
    -

    출력 예시:

    -
    [INFO] 임시 디렉토리 생성: /tmp/tmp_xxx
    -[INFO] 임시 파일 경로: /tmp/tmp_xxx/output.parquet
    -[INFO] 사용할 writer: to_parquet (format: parquet)
    -[INFO] 데이터 임시 파일에 저장 완료: /tmp/tmp_xxx/output.parquet
    -[INFO] 원자적 교체 완료: /tmp/tmp_xxx/output.parquet -> output.parquet
    -[INFO] _SUCCESS 플래그 파일 생성: output.parquet._SUCCESS
    -[INFO] Atomic write completed successfully (took 0.2359s)
    -
    -
    -
    -
    -

    상세 진단 모드 (verbose=True):

    -
    # 상세한 성능 진단 정보 출력
    -aw.write(df, "output.parquet", format="parquet", verbose=True)
    -
    -
    -

    출력 예시:

    -
    [INFO] 임시 디렉토리 생성: /tmp/tmp_xxx
    -[INFO] 임시 파일 경로: /tmp/tmp_xxx/output.parquet
    -[INFO] 사용할 writer: to_parquet (format: parquet)
    -[INFO] 데이터 임시 파일에 저장 완료: /tmp/tmp_xxx/output.parquet
    -[INFO] 원자적 교체 완료: /tmp/tmp_xxx/output.parquet -> output.parquet
    -[INFO] _SUCCESS 플래그 파일 생성: output.parquet._SUCCESS
    -[DEBUG] Atomic write step timings (SUCCESS): setup=0.0012s, write_call=0.2345s, replace=0.0001s, success_flag=0.0001s, total=0.2359s
    -
    -
    -
    +

    Atio 🛡️는 안전하고 원자적인 파일 쓰기를 지원하는 경량 Python 라이브러리입니다.

    +

    Pandas, Polars, NumPy 등 데이터 객체 저장 시 파일 손상 없이, **트랜잭션처럼 안전하게 처리**할 수 있습니다.

    -

    오류 발생 시 (기본 사용법):

    -
    [INFO] 임시 디렉토리 생성: /tmp/tmp_xxx
    -[INFO] 임시 파일 경로: /tmp/tmp_xxx/output.parquet
    -[INFO] 사용할 writer: to_parquet (format: parquet)
    -[ERROR] 임시 파일 저장  예외 발생: [Errno 28] No space left on device
    -[INFO] Atomic write failed during write stage (took 0.1246s, error: OSError)
    -
    -
    +

    주요 기능

    +
    +
    🔒 Atomic File Writing

    Safe writing using temporary files

    +
    +
    📊 Multiple Format Support

    CSV, Parquet, Excel, JSON, etc.

    +
    +
    🗄️ Database Support

    Direct SQL and Database writing

    +
    +
    📈 Progress Display

    Progress monitoring for large data processing

    +
    +
    🔄 Rollback Function

    Automatic recovery when errors occur

    +
    +
    🎯 Simple API

    Intuitive and easy-to-use interface

    +
    +
    📋 Version Management

    Snapshot-based data version management

    +
    +
    🧹 Auto Cleanup

    Automatic deletion of old data

    +
    +
    -

    오류 발생 시 (verbose=True):

    -
    [INFO] 임시 디렉토리 생성: /tmp/tmp_xxx
    -[INFO] 임시 파일 경로: /tmp/tmp_xxx/output.parquet
    -[INFO] 사용할 writer: to_parquet (format: parquet)
    -[ERROR] 임시 파일 저장  예외 발생: [Errno 28] No space left on device
    -[DEBUG] Atomic write step timings (ERROR during write): setup=0.0012s, write_call=0.1234s (실패), replace=N/A, success_flag=N/A, total=0.1246s, error_type=OSError
    -
    -
    -

    측정되는 단계:

    -
      -
    • setup: 임시 폴더 생성 및 초기 설정

    • -
    • write_call: 실제 데이터 쓰기 함수 호출 (대부분의 시간 소요)

    • -
    • replace: 원자적 파일 교체

    • -
    • success_flag: _SUCCESS 플래그 파일 생성

    • -
    • total: 전체 작업 시간

    • -
    -

    지원하는 오류 상황:

    -
      -
    • KeyboardInterrupt: 인터럽트 발생 시점과 소요 시간 표시

    • -
    • 권한 오류: 파일 시스템 권한 문제 진단

    • -
    • 디스크 공간 부족: 저장 공간 부족 상황 진단

    • -
    • 메모리 부족: 메모리 압박 상황 진단

    • -
    • 네트워크 오류: 네트워크 드라이브 접근 문제 진단

    • -
    • 지원하지 않는 형식: 잘못된 파일 형식 지정 시 진단

    • -
    • 동시 접근 오류: 멀티스레딩 환경에서의 충돌 진단

    • -
    -

    장점:

    -
      -
    • 🎯 정확한 병목점 파악: Atio 오버헤드 vs 실제 쓰기 작업 시간 구분

    • -
    • 🔧 성능 최적화 가이드: 어느 단계에서 시간이 많이 소요되는지 명확히 표시

    • -
    • 🐛 디버깅 시간 단축: 문제의 원인을 빠르게 파악 가능

    • -
    • 📊 성능 모니터링: 대용량 데이터 처리 시 성능 추적

    • -
    • 🚨 오류 진단: 실패 상황에서도 정확한 원인과 발생 시점 파악

    • -
    -
    -
    -
    -
    -

    🧠 왜 이 도구가 정말 중요한가요?

    -

    NumPy나 Pandas는 데이터 분석에서는 최적이지만, 파일로 저장할 때는 아래와 같은 위험이 있습니다:

    -
      -
    1. 파일 일부만 저장되어 깨질 수 있음 — 강제 종료나 오류 시

    2. -
    3. 동시 쓰기 충돌 — 멀티프로세스 환경에서 파일이 엉킬 수 있음

    4. -
    5. 플랫폼 간 동작 차이 — Windows와 Linux/macOS에서 파일 시스템 동작이 다름

    6. -
    -

    AtomicWriter는 임시 파일에 쓰고 단일 rename()/replace() 작업으로 교체합니다.
    -이 방식은 “완전히 저장되거나 전혀 저장되지 않는” 원자성(atomicity)을 보장하며,

    -
      -
    • POSIX: os.replace (atomic), fsync

    • -
    • Windows: MoveFileEx, Commit
      -를 활용하여 파일이 항상 일관된 상태를 유지하도록 합니다 :contentReference[oaicite:1]{index=1}.

    • -
    -
    -
    -
    -

    ⚙️ 설치

    -
    pip install atomicwriter
    -
    -## 🛠️ 사용 예제
    -
    -```python
    -import atomicwriter as aw
    -import pandas as pd
    -
    -df = pd.DataFrame({"a": [1, 2, 3]})
    -
    -# 기본 사용법
    -aw.write(df, "output.parquet", format="parquet")
    -# │→ 임시 파일 작성 → 원자적 교체 → _SUCCESS 생성
    -# │→ 실패 시 원본 보존, 임시 파일 자동 정리
    +

    빠른 시작

    +
    import atio
    +import pandas as pd
     
    -# 상세 성능 진단 로깅 활성화
    -aw.write(df, "output_verbose.parquet", format="parquet", verbose=True)
    -# │→ 각 단계별 실행 시간 측정 및 로그 출력
    +# 간단한 DataFrame 생성
    +df = pd.DataFrame({
    +    "name": ["Alice", "Bob", "Charlie"],
    +    "age": [25, 30, 35],
    +    "city": ["Seoul", "Busan", "Incheon"]
    +})
     
    -# 진행도 표시와 함께 사용
    -aw.write(df, "output_progress.parquet", format="parquet", show_progress=True)
    -# │→ 실시간 진행도 표시
    +# 안전한 파일 쓰기
    +atio.write(df, "users.parquet", format="parquet")
     
    -# 모든 옵션 조합
    -aw.write(df, "output_full.parquet", format="parquet", 
    -         verbose=True, show_progress=True)
    -# │→ 성능 진단 + 진행도 표시
    +# 진행도 표시와 함께 저장
    +atio.write(df, "users.csv", format="csv", show_progress=True)
     
    -
    -

    💡 빅데이터 워크플로우에서 활용 시나리오

    - +
    +

    지원하는 형식

    +
    + ++++++ - - - + + + + - - - + + + + + + + + + + + + + + + + + + + + + + + + - - - + + + + - - - + + + + + + + + + + + + + +
    지원하는 파일 형식

    시나리오

    해결 방법

    장점

    형식

    Pandas

    Polars

    NumPy

    Pandas → CSV 저장

    임시 파일에 기록 후 교체

    CSV 파일 깨짐 방지

    CSV

    Parquet

    Excel

    JSON

    Pickle

    멀티프로세스 병렬 쓰기

    atomic replace 방식 사용

    충돌 없는 안전 저장

    HTML

    데이터 파이프라인 작업

    저장 성공 시 _SUCCESS 확인

    데이터 완전성 보장

    SQL

    Database

    NPY/NPZ

    -
    -
    -

    🔄 비교 – 유사 라이브러리 특징 정리

    -
    -

    python-atomicwrites

    -
      -
    • 간편한 API

    • -
    • Windows 지원

    • -
    • 크로스 플랫폼 호환

    • -
    +
    +

    사용 사례

    +
    +
    🔹 데이터 파이프라인

    ETL 과정에서 중간 데이터 안전하게 저장

    +
    +
    🔹 실험 데이터 관리

    머신러닝 실험 결과의 버전 관리

    +
    +
    🔹 대용량 데이터 처리

    대용량 파일의 안전한 저장 및 진행도 모니터링

    +
    +
    🔹 데이터베이스 연동

    Pandas/Polars 데이터를 SQL/NoSQL DB에 안전하게 저장

    +
    +
    -
    -

    atomicwriter (본 프로젝트)

    -
    -
    -
    -

    ✅ 라이선스

    -

    Apache 2.0 — 기업 및 커뮤니티 모두 자유롭게 사용 가능

    -
    -
    -
    -

    ✨ 요약

    -

    AtomicWriter는 분석만큼 중요한 “저장” 단계를 안전하게 처리하는 도구입니다.

    -

    특히 데이터 무결성이 중요한 환경에서
    -(예: 머신러닝 배치, 멀티프로세스 분석, 중요 로그 저장 등)
    -작지만 강력한 해결책을 제공합니다.

    -

    📘 시나리오 1: Pandas CSV 저장 중 작업 중단 -문제 상황: -한 사용자가 Pandas로 대용량 분석 결과를 .csv 파일로 저장하던 중, 예상치 못한 전원 차단이나 커널 강제 종료가 발생했습니다. -결과 파일은 50MB 중 3MB만 저장된 채 손상되었고, 이후 읽기도 되지 않았습니다.

    -

    AtomicWriter로 해결: -임시 파일에 먼저 기록 후, 모든 쓰기가 성공해야만 원본과 교체됩니다. -따라서 중간에 꺼져도 기존 파일은 보존되고, 손상된 임시 파일은 자동 정리되어 안정성을 확보할 수 있습니다.

    -

    📘 시나리오 2: 멀티프로세스 환경에서 경쟁 조건(Race Condition) -문제 상황: -Python multiprocessing 기반 데이터 수집 파이프라인에서 여러 프로세스가 동시에 같은 파일을 저장하며 충돌이 발생했습니다. -결과적으로 로그 파일이 덮어쓰여 누락되거나, 일부 JSON 파일은 파싱할 수 없는 손상된 형태로 저장됐습니다.

    -

    AtomicWriter로 해결: -파일 쓰기를 atomic replace 방식으로 수행하면, 한 번에 하나의 프로세스만 최종 경로로 이동할 수 있습니다. -이로써 경쟁 조건 없이 충돌 없이 저장이 보장됩니다.

    -

    📘 시나리오 3: 데이터 파이프라인 검증 불가 -문제 상황: -ETL 작업에서 .parquet 저장이 완료됐는지 여부를 자동 시스템이 판단할 수 없어, 손상되거나 미완성된 데이터를 다음 단계에서 그대로 사용했습니다. -결과적으로 모델 학습 데이터에 결측값이 포함되어 품질 저하가 발생했습니다.

    -

    AtomicWriter로 해결: -저장이 성공적으로 완료된 경우에만 _SUCCESS 플래그 파일을 함께 생성하도록 설정할 수 있습니다. -후속 단계는 _SUCCESS 유무를 기준으로 안전하게 파이프라인을 구동할 수 있습니다.

    -

    📘 시나리오 4: Polars DataFrame을 S3로 저장 중 오류 발생 -문제 상황: -Polars DataFrame을 AWS S3에 직접 저장하는 중간에 ConnectionError가 발생하여 S3에는 부분적으로 깨진 .parquet 파일이 올라갔습니다. -다음 번 실행에서 이 파일을 재사용하려 했지만, S3에서 파일이 손상된 채로 존재해 오류를 유발했습니다.

    -

    AtomicWriter로 해결: -로컬 임시 파일에 완전히 저장된 후에만 S3 업로드 또는 교체가 수행됩니다. -네트워크 이슈나 디스크 오류에도 최종 파일은 항상 완전한 상태로만 존재하게 됩니다.

    + +
  • 빠른 시작 +
  • +
  • 사용 예제 +
  • +
  • 고급 사용법 +
  • + +
    +
    +
    +

    API Reference

    +
    +

    핵심 함수들

    +

    Atio: 안전한 원자적 파일 쓰기 라이브러리

    +

    progress 적용 후 write 함수

    +
    +
    +atio.core.write(obj, target_path=None, format=None, show_progress=False, verbose=False, **kwargs)[소스]
    +

    데이터 객체(obj)를 안전하게 target_path 또는 데이터베이스에 저장합니다.

    +
      +
    • 파일 기반 쓰기 (format: ‘csv’, ‘parquet’, ‘excel’ 등): +- target_path (str): 필수. 데이터가 저장될 파일 경로입니다. +- 롤백 기능이 있는 원자적 쓰기를 수행합니다.

    • +
    • 데이터베이스 기반 쓰기 (format: ‘sql’, ‘database’): +- target_path: 사용되지 않습니다. +- kwargs (dict): 데이터베이스 쓰기에 필요한 추가 인자들입니다.

      +
      +
        +
      • pandas.to_sql: ‘name’(테이블명), ‘con’(커넥션 객체)가 필수입니다.

      • +
      • polars.write_database: ‘table_name’, ‘connection_uri’가 필수입니다.

      • +
      +
      +
    • +
    +
    +
    매개변수:
    +
      +
    • obj – 저장할 데이터 객체 (e.g., pandas.DataFrame, polars.DataFrame, np.ndarray).

    • +
    • target_path (str, optional) – 파일 저장 경로. 파일 기반 쓰기 시 필수. Defaults to None.

    • +
    • format (str, optional) – 저장할 포맷. Defaults to None.

    • +
    • show_progress (bool) – 진행도 표시 여부. Defaults to False.

    • +
    • verbose (bool) – 상세한 성능 진단 정보 출력 여부. Defaults to False.

    • +
    • **kwargs – 각 쓰기 함수에 전달될 추가 키워드 인자.

    • +
    +
    +
    +
    + +
    +
    +atio.core.write_snapshot(obj, table_path, mode='overwrite', format='parquet', **kwargs)[소스]
    +
    + +
    +
    +atio.core.read_table(table_path, version=None, output_as='pandas')[소스]
    +
    + +
    +
    +atio.core.expire_snapshots(table_path, keep_for=datetime.timedelta(days=7), dry_run=True)[소스]
    +

    설정된 보관 기간(keep_for)보다 오래된 스냅샷과 +더 이상 참조되지 않는 데이터 파일을 삭제합니다.

    +
    + +
    +
    +atio.plugins.register_writer(obj_type, fmt, handler)[소스]
    +

    (객체 타입, 포맷) 쌍으로 쓰기 핸들러를 등록

    +
    + +
    +
    +atio.plugins.get_writer(obj, fmt)[소스]
    +

    객체의 타입과 포맷에 맞는 핸들러를 조회

    +
    + +
    +
    +atio.utils.setup_logger(name='atio', debug_level=False)[소스]
    +
    + +
    +
    +atio.utils.check_file_exists(path)[소스]
    +
    + +
    +
    +class atio.utils.ProgressBar(filepath: str, stop_event: Event, description: str = 'Writing')[소스]
    +

    파일 쓰기 진행 상황을 콘솔에 표시하는 클래스. +스피너, 처리된 용량, 처리 속도, 경과 시간을 표시합니다.

    +
    +
    +__init__(filepath: str, stop_event: Event, description: str = 'Writing')[소스]
    +
    + +
    +
    +run()[소스]
    +

    진행도 막대를 실행하는 메인 루프. +이 함수가 모니터링 스레드에서 실행됩니다.

    +
    + +
    + +
    +
    +atio.utils.read_json(path: str)[소스]
    +
    + +
    +
    +atio.utils.write_json(data: dict, path: str)[소스]
    +
    + +
    +
    +
    +

    Indices and tables

    +