Python ve FTP Dosya İşlemleri

Request, os ve ftp paket kullanım örneği

Son yayınladığım Veri Ambarı (Data Warehouse) Nedir? başlıklı yazıdan bu yana tam 50 gün geçmiş. Yazıların seyrekleşmesi ve yeni yazı frekansının düşmesi ile ilgili pek çok nedeni var elbette.

AA

2021 Hedefleri başlıklı yazıda da belirttiğim üzere bu yıl ağırlıklı olarak eğitimleri önceliklendirmiş olmam. Önümüzdeki zaman içerisinde bu konuya dair paylaşımlarım olacak. Bu paylaşımlardan ilki olarak, bir Python örneğine değinmek istiyorum.

Python İle Basit Request ve FTP İşlemi

Grav Yeni Tema ve Peformans Değişimi başlıklı yazıda, şu anda görüntülemekte olduğunuz bu tema ile ilgili olarak Parsedown ve Parsedown Extra dosyalarında çeşitli özelleştirmeler yapmıştım. Ancak, her yeni Grav sürümünde maalesef bu değişiklikler kaybolduğu için (dosya yedekleri ile süreci yönetiyordum) tekrar tekrar bazı işlemleri yapmak durumunda kalıyordum. Sistem dosyaları Git reposu dışında kaldığı için işlemleri SSH veya FTP üzerinden bağlantı kurarak yönetmek gerekiyordu.

Grav üzerinden ilgili classextend ederek işlemler yapmak mümkün görünüyor. Ancak, daha net bir çözüm sağlayamadım. Geçici bir çözüm arayışı içerisinde, şu sıralar öğrenmek için yoğun bir şekilde vakit ayırdığım Python ile örneklendirme fikri geldi.

flowchart TD Python --> Request --> Python Request --> Gist_1(Gist: Parsedown) & Gist_2(Gist: Parsedown-extra) Python --> os os --> local(Local: ./tmp) Python --> FTP & local local --> FTP

Parsedown1 ve Parsedown Extra2 için 2 ayrı gist üzeriden yaptığım değişiklikleri benzer değişiklikleri yapma ihtiyacı duyanlar için paylaşmıştım. Dolayısıyla, sürece yine bu gist'leri dahil edeceğim. Ek olarak, süreci sadece bu 2 dosya ile tutmak istemiyorum. Bu nedenle, doğrudan dosya tanımlamak yerine içeriği dict olarak iletmeye karar verdim.

from ftplib import FTP
import os
import requests

class ParsedownFTPSyc:
    def __init__(self, FTP, GIST, CHUNKSIZE=100):
        self.FTP = FTP
        self.GIST = GIST
        self.CHUNKSIZE = CHUNKSIZE

    def createLocalDir(self, whr):
        if os.path.exists(whr) is False:
            os.mkdir(whr)
        os.chdir(whr)

    def getGists(self, whr='/private/tmp/parsedown/'):
        self.createLocalDir(whr)

        for dirs in self.GIST['files'].items():
            currGist = dirs[1]

            gistURL = f'%s/%s/%s' % (self.GIST['user'],
                                     currGist['gistid'],
                                     currGist['file'])

            try:
                req = requests.get(gistURL, allow_redirects=True)
            except Exception as err:
                print(err)
            else:
                if req.status_code == 200:
                    with open(currGist['file'], 'wb') as fd:
                        for chunk in req.iter_content(self.CHUNKSIZE):
                            fd.write(chunk)
                        print('%s is OK' % currGist['file'])

    def writeFTPFile(self, frm):

        try:
            with FTP(self.FTP['host']) as ftp:
                ftp.login(user=self.FTP['user'], passwd=self.FTP['pass'])
                ftp.cwd(self.FTP['dirn'])

                for dirs in self.GIST['files'].items():

                    FILE = dirs[1]['file']
                    ftp.cwd(self.FTP['dirn'] + dirs[0])
                    with open(frm + FILE, 'rb') as f:
                        ftp.storlines(f"STOR {FILE}", f)

                ftp.quit()

        except Exception as err:
            print(err)

def main():

    newSync = ParsedownFTPSyc(
        FTP={
            'host': 'ftp.domain.com',
            'user': 'user@domain.com',
            'pass': 'password123',
            'dirn': "/app/",
        },
        GIST={
            'user': 'https://gist.githubusercontent.com/ceaksan',
            'files': {
                'parsedown': {
                    'gistid': '194ff03625f6050dc2959ec062b0c68a/raw/c2be5d72237a46f56692ae17716c0e9739070fb7',
                    'file': 'Parsedown.php'
                },
                'parsedown-extra': {
                    'gistid': '99d17f8d6691f8948f701468e6611a34/raw/cfc87d141d258838576635105b7ebc277e4affa8',
                    'file': 'ParsedownExtra.php'
                }
            }
        })

    newSync.getGists(whr='/private/tmp/parsedown/')
    newSync.writeFTPFile(frm='/private/tmp/parsedown/')

if __name__ == '__main__':
    main()

Evet, görüldüğü üzere bir de FTP paketi kullanılmakta. Kod parçacığının yaptığı iş öncelikle GIST içerisinde belirtilen kullanıcı tarafından paylaşılmış olan gist'leri lokal ortamda, belirtilen dizine indirmek.

Ardından, indirilen bu dosyaları FTP içerisinde belirtilen adrese yüklemek. dirn elbette gist özelinde de belirtilebilir. Ancak, örnekteki 2 dosya aynı dizin altında yer aldığı için örnekte tek bir property değeri üzerinden ilerledim.

Son olarak, bu konu pratik bir şekilde nasıl çalıştırabileceğimize bakalım.

Elbette python, ipython veya jupyter başta olmak üzere pek çok ortam aracılığı ile işlem gerçekleştirmek mümkün. Ancak, doğrudan bir dosya tıklaması ya da komut satırı ifadesi ile de ilerlenebilir.

Yukarıdaki kod parçacığının ilk satırına #!/usr/bin/env python3 eklemesini yaptığınızda ilgili kod artık ./dosya-adi.py şeklinde de çalıştırılabilir. python3 yerine kullanılan sürüm yazılabilir.

#!/usr/bin/env python3

from ftplib import FTP
import os
import requests

...

Bunun yanı sıra, bir shell script dosyası aracılığı ile de kod parçacığının işleme alınması sağlanabilir.

#!/bin/sh
python3 dosya-adi.py

Seçilen yöntem hangisi olduğu fark etmeksizin komut satırı için bir alias oluşturabiliriz. Bunun için şu adımları izlemek yeterli olacaktır.

chmod +x dosya-adi.py
alias parsedown="/dosya-dizini/dosya-adi.py" # ya da .sh dosya yolu
source ~/.zshrc

Evet, bu işlemle birlike artık komut satırına parsedown yazmamız durumunda ilgili dosyalara ait gist'ler indirilecek ve FTP aracılığı ile belirtilen dizine yüklenecektir.

İlerleyen zaman içerisinde Python ile ilgili edindiğim yeni bilgiler doğrultusunda bu kod parçacığını güncellemeye devam edeceğim. Kod ile ilgili önerileriniz olursa değerlendirmekten memnuniyet duyarım.