Yazdığım kod ne kadar kaliteli?

Merhabalar, bir proje geliştiriyorum. Projenin kaynak kodları sıkıntısız çalışıyor ancak projeyi her çalıştırdığımda kendime kaynak kodlarının gözüme pek çirkin geldiği halde işlevinin olduğunu itiraf etmek zorunda kalıyorum. Bir kodun kalitesinin biraz öznel bir kavram olduğunu biliyorum ancak bu projenin kaynak kodunun insanlar ya da standartlara göre kalitesini bilmiyorum. Bu yüzden de bir çıkmaza girip projeyi bir bu şekilde bir de öbür şekilde tekrar tekrar yazıp duruyorum. Sonuç olarak, yazdığım kodun “genel” kalitesini öğrenip nasıl bu çıkmazdan çıkabilirim?

Bence kodun kalitesi tamamen performansla alakalı yani gereksiz bir işlem felan yoksa takılma buna çok fazla başka şeylerle uğraş. Kalite dediğin şey fonkşsyon değişken isimleriyse kodunu bir başkasının okuması gerekmiyorsa projede değiştirmene gerek yok sürekli yazınca insanın eli yazıyor düzgün isim vermeye başlıyor zaten

Aslında kaliteden kastım değişken isimleri değildi (Onlar da önemli tabi.). Yazdığım kodu daha kısa, düzenli ve göze güzel hale getirebileceğimi düşünüp aynı işlevi yerine getiren kodu tekrar ve tekrar yazıyorum. Acaba kodda optimize olmayan yerler var mı, projenin yaptığı işlemleri iyi logluyor muyum soruları da cabası.

İyi bir geliştirici olma yolundaysan, ister istemez brainfuck’a doğru giden yolu arşınlamaya başlarsın.

Ve iyi bir geliştirici, hatta bir bilim insanı, en sade haliyle bir çocuk şu soruyu sormaktan asla kendini alıkoyamaz:

Annem bu kadar güzel pasta yapmayı nerden öğrendi?

1 Beğeni

Temiz kod yazma gayesi cok guzel. Kod yazarken aslinda onemli olan sey zaten teknikler, paradigmalar, konseptlerdir. Kod yazdigin paradigmaya, stile hakim olduktan, o stilde kod okuyabildikten sonra temiz kod yazabiliyorsun. Ornegin temiz buldugun python kodu ile kendi kodunu karsilastir. Kodunda basitlestirmeler yap, gozune karmasik gozuken yerlerin mutlaka ustune git, kullandigin dile, teknige, paradigmaya gore uygun bir cozum mutlaka bulacaksin.

Aslinda baktiginda zaten isin programcilik tarafi da bu. Kod yaziyorsun, sadece islevi onemli olsa programlama dilleri sadece isin en teknik tarafiyla ugrasirdi, muhtemelen sadece prosedurel programlama var olurdu. Sadece derleyiciler, yorumlayicilar, platform gibi kavramlar on planda olurdu ama aksine kod yazarken maintainability kavrami asil gayedir. Koda bir seyler eklemen gerektiginde cok ufak duzeltmelerle, eklemek icin cikarma yapmadan isini halledebiliyor olman gerekiyor. Bunun yaninda kodun okunakli olmasi da gerekiyor ama bu oldukca goreceli. Bana gore (ve haskell kullanmis insanlara) su kod super okunakli mesela:

-- https://www.codewars.com/kata/5287e858c6b5a9678200083c/haskell
module Narcissistic where
import Data.Char (digitToInt)

narcissistic :: Integral n => n -> Bool
narcissistic n = (== n) . fromIntegral . sum . map (\x -> x^len) $ digits
  where digits = map digitToInt . show . toInteger $ n
        len = length digits

Ve maintain edilebilir bir kod. Ama fonskiyonel programlamayla pek hasir nesir olmadiysaniz muhtemelen su kodu tercih edersiniz:

# (alintidir)
def narcissistic( value ):
    value = str(value)
    size = len(value)
    sum = 0
    for i in value:
        sum += int(i) ** size
    return sum == int(value)

Ilk kod haskell ile yazilmis ve fonksiyonel programlama paradigmasi kullanilmis. Diger kod ise python ve klasik imperatif stil. Tabii bunlar iki ayri paradigma, kendi icinde okunakli/okunaksiz kodlar da var, mesela su post cok guzel bir ornek:

Bir baska haskell ornegi (deklaratif):

getLine >>= readFile >>= putStrLn

Ayni kodun ayni dilde imperatif hali:

main = do
    filename <- getLine
    contents <- readFile filename
    putStrLn contents

Ilk iki paragraftaki terimler hakkinda pek emin degilim, ifade etmeye calistigim seyi anlamissinizdir. Onerilere acigim.

4 Beğeni

Aslında neyi sevip neyi sevmediğimi biliyorum. Amacım dediğiniz gibi kodu sonradan rahat bir şekilde düzenleyebilmek, geliştirmek. Ancak projede zorunluluktan kullandığım try except yapısının çok çirkin olduğunu düşündüğümden dolayı kodu tekrar ve tekrar baştan yazıyorum. Ayrıca kod da çok girintilemeyi de pek sevmiyorum ancak kodu try except olmadan nasıl yazacağımı bilmiyorum:

from traceback import format_exc
from sys import exit
from utils.process import kill, start, process_list
from utils.env_paths import *
from utils.log import Log
from runtools.registry import *
from runtools.server import *
from runtools.file import *
from winreg import HKEY_CURRENT_USER
from win32api import MessageBox
from win32con import MB_OK, MB_YESNO, MB_ICONERROR

if __name__ == "__main__":

    VERIFICATE_SSL = True

    if not check_key(HKEY_CURRENT_USER,"SOFTWARE\\Player","version"):

        install_log = Log("Installer")
        install_log.write("Getting app contents from Github...")
        try:
            content_bytes = get_program_contents(timeout=3,verify=VERIFICATE_SSL)
        except Exception as exc:
            exc_type = exc.__class__.__name__.lower()
            install_log.write(f"An error occured while getting app contents from Github:\n{format_exc()}\n")

            if exc_type == "connectionerror":
                MessageBox(0,"İnternet bağlantısı mevcut olmadığından program karşıdan yüklenemedi.", "Player| Kurulum", MB_OK | MB_ICONERROR)
                exit(1)

            elif exc_type == "connecttimeout":
                MessageBox(0,"İnternet bağlantınız yavaş olduğundan sunucu ile iletişim zaman aşımına uğradı.", "Player| Kurulum", MB_OK | MB_ICONERROR)
                exit(1)

            elif exc_type == "sslerror":
                req_without_verify = MessageBox(0,"Sunucu ile SSL sertifikası doğrulaması başarısız oldu. SSL sertifika doğrulamasını pas geçip gene de devam etmek istiyor musunuz?\n\n(Bu durum güvenlik açıklarına sebep olabileceğinden tavsiye edilmez.)", "Player| Kurulum", MB_YESNO | MB_ICONERROR)

                if req_without_verify == 6:
                    VERIFICATE_SSL = False
                    install_log.write("Attempting again to get version information from Github...")
                    try:
                        content_bytes = get_program_contents(timeout=3, verify=VERIFICATE_SSL)
                    except Exception as exc:
                        install_log.write(f"Attempt failed:\n{format_exc()}\n")
                        MessageBox(0,"Sunucu ile bağlantı kurulurken hata oluştu. Programı kişisel ağ üzerinde kurmayı deneyin.", "Player| Kurulum", MB_OK | MB_ICONERROR)    
                        exit(1)
                else:
                    exit(1)

        try:
            install_log.write(f"Creating temporary folder: \"{SETUP_PATH}\"")
            create_dir(SETUP_PATH)

            install_log.write("Downloading app contents...")
            write_byte(f"{SETUP_PATH}\\executable.zip","wb",content_bytes)
            install_log.write("App contents have downloaded.")

            install_log.write(f"Creating contents' folder: \"{PROGRAM_PATH}\"")
            create_dir(PROGRAM_PATH)
            install_log.write(f"Contents' folder created: \"{PROGRAM_PATH}\"")

            install_log.write("Extracting contents from temporary zip file...")
            extract(f"{SETUP_PATH}\\executable.zip",PROGRAM_PATH)
            install_log.write("Contents have extracted from zip file.")

            install_log.write("Deleting temporary setup folder...")
            removeall(SETUP_PATH)
            install_log.write("Temporary setup folder been deleted.")

            install_log.write("Creating registry value at: \"HKEY_CURRENT_USER\\SOFTWARE\\Player\\version\"")
            create_key(HKEY_CURRENT_USER,"SOFTWARE\\Player","version",get_version())
            install_log.write("Created registry value at: \"HKEY_CURRENT_USER\\SOFTWARE\\Player\\version\"")

        except PermissionError as exc:
            install_log.write(f"An error occured due to system permissions:\n{format_exc()}\n")
            MessageBox(0,"Yetki hatası. Uygulamayı yönetici olarak çalıştırmayı deneyin.", "Player| Kurulum", MB_OK | MB_ICONERROR)
            exit(1)

        except Exception as exc:
            exc_tb = format_exc()
            install_log.write(f"An unexpected error occured:\n{exc_tb}\n")
            MessageBox(0,f"Beklenmedik bir hata oluştu:\n\n{exc_tb}", "Player| Kurulum", MB_OK | MB_ICONERROR)
            exit(1)

        else:
            install_log.write("Installation completed without any issues.")

    else:  

        updater_log = Log("Updater")
        updater_log.write("Getting version information from Github...")

        CONTINUE_UPDATE = False
        try:
            local_version = read_key(HKEY_CURRENT_USER,"SOFTWARE\\Player","version")
            main_version = get_version()
        except Exception as exc:
            exc_type = exc.__class__.__name__.lower()
            updater_log.write(f"An error occured while getting version information:\n{format_exc()}\n")

            if exc_type == "sslerror":
                req_without_verify = MessageBox(0,"Sunucu ile SSL sertifikası doğrulaması başarısız oldu. SSL sertifika doğrulamasını pas geçip gene de güncellemeye devam etmek istiyor musunuz?\n\n(Bu durum güvenlik açıklarına sebep olabileceğinden tavsiye edilmez.)", "Player| Güncelleme Sistemi", MB_YESNO | MB_ICONERROR)

                if req_without_verify == 6:
                    VERIFICATE_SSL = False
                    updater_log.write("Attempting again to get version information from Github...")
                    try:
                        content_bytes = get_program_contents(timeout=3, verify=VERIFICATE_SSL)
                    except Exception as exc:
                        updater_log.write(f"Attempt failed:\n{format_exc()}\n")
                    else:
                        CONTINUE_UPDATE = True
        else:
            CONTINUE_UPDATE = True


        if CONTINUE_UPDATE:
            if local_version != main_version:
                updater_log.write("Application isn't up to date.")
                try:
                    if any(b"main" in i for i in process_list()):
                        kill("main.exe")
                        updater_log.write("Killed 'main.exe'")

                    updater_log.write("Getting app contents from Github...")
                    content_bytes = get_program_contents(timeout=3,verify=VERIFICATE_SSL)

                    updater_log.write(f"Creating temporary folder: \"{TEMP_PATH}\"")
                    create_dir(TEMP_PATH)

                    updater_log.write("Downloading app contents...")
                    write_byte(f"{TEMP_PATH}\\executable.zip","wb",content_bytes)
                    updater_log.write("App contents have downloaded.")

                    updater_log.write("App folder is cleaning...")
                    removeall(PROGRAM_PATH)
                    create_dir(PROGRAM_PATH)
                    updater_log.write("App folder been cleaned.")

                    updater_log.write("Extracting contents from temporary zip file...")
                    extract(f"{TEMP_PATH}\\executable.zip",PROGRAM_PATH)
                    updater_log.write("Contents have extracted from zip file.")

                    updater_log.write("Deleting temporary folder...")
                    removeall(TEMP_PATH)
                    updater_log.write("Temporary folder been deleted.")

                    updater_log.write("Updating registry value at: \"HKEY_CURRENT_USER\\SOFTWARE\\Player\\version\"")
                    create_key(HKEY_CURRENT_USER,"SOFTWARE\\Player","version",get_version())
                    updater_log.write("Updated registry value at: \"HKEY_CURRENT_USER\\SOFTWARE\\Player\\version\"")

                except PermissionError as exc:
                    MessageBox(0,"Yetki hatası. Uygulamayı yönetici olarak çalıştırmayı deneyin.", "Player| Güncelleme Sistemi", MB_OK | MB_ICONERROR)
                    exit(1)

                except Exception as exc:
                    exc_tb = format_exc()
                    MessageBox(0,f"Beklenmedik bir hata oluştu:\n\n{exc_tb}", "Player| Güncelleme Sistemi", MB_OK | MB_ICONERROR)
                    exit(1)

                else:
                    updater_log.write("Update completed without any issues.")

            elif local_version == main_version:
                updater_log.write("Application is already up to date.")
                updater_log.open()


    if not any(b"main.exe" in i for i in process_list()):
        start(f"{PROGRAM_PATH}\\main.exe")

    exit(0)

Bu gibi durumlar için kullanabileceğiniz bazı yollar bulunuyor:

DRY:Don’t repeat yourself
Bu yaklaşımda bir kodu sürekli aynı şekillerde yazmak yerine ana bir alana aynı işlevi yapan bir fonksiyon ekleyebilirsiniz ve bu kodunuzu daha okunaklı ve kaliteli yapar(Amerika’yı tekrar keşfetmeye gerek yok)

Parçalara ayırma:
Eğer ana dosyanızda çok fazla fonksiyon ve try-except çalıştırmak istemiyorsanız işinize yarayabilir. Unutmayın herzaman tek bir dosya ile çalışmak zorunda değilsiniz. Hem projenizi parçalara bölmek kod yönetimini ve hata onarımını büyük oranda arttırır ve kodunuzun daha kaliteli olmasını sağlar.

Mesela burdaki

ifadesini sadece fonksiyon çağırmakta kullanabilirsiniz ya da hiç uğraşmadan sınıflardan yararlanabilirsiniz.

Kolay gelsin :slight_smile:

1 Beğeni

“Kalite” subjektif bir kavram oldugundan dolayi guvenilir subjelere ihtiyac var. Kodu review etmeye hazir insanlar, arkadaslar, kodu yazan kisinin gelecekteki hali olabilir. Belirli bir noktadan sonra kisi kendisine kodu yazarken de danisabilir.

Yuksek bir standarda tabi code review istersen (moralini bozmasina izin vermezsen) yapayim:

Bu yildizlarin icinde kimbilir neler var. Koddaki herhangi bir fonksiyon, bunlarin herhangi birinden geliyor olabilir. Hic sevdigim bir stil degil; from x import y bile kullanmiyorum. (Cunku cogunlukla x.y daha okunakli)

Bu onlarca satir bir main fonksiyonuna alinarak baslanmali. Sahsen Rust gibi refactor etmesi zor diller disinda fonksiyonlarin bir ekrani gecmesini sevmiyorum, okunakliligi azaltiyor.

Fonksiyonun ismi eksik olmus. Neyi check ettigini, hangi kosulda T/F dondurdugunu bilmemize imkan yok. *'lardan dolayi herhangi bir modulden gelmis olabilir.

Bu neden fonksiyonun basinda initialize edilmiyor? Cunku fonksiyon yok. main de degil, install fonksiyonunda olmali bu.

get_program_contents isimli bir fonksiyonun program parametresini almasini beklerim.

except ConnectionError: … except ConnectTimeout: … olmuyor mu?
instanceof(ConnectionError, e) olmuyor mu?

Exception handler icinde kod yazmayi sevmiyorum ama alternatifleri her zaman daha guzel olmayabiliyor. Her halukarda yapilacak isler tek fonksiyon cagrisina indirilmeli. download_without_verification?

Constant’in degerini degistirmisiz. Olmaz.

Diger kod bloklari icin de yukarida yazdiklarim gecerli.

main nedir, main.exe nedir? Bu string’lere isim verilmeli THIS_PROGRAM_NAME gibi.

Bilgisayarimda baska bir is icin yazdigim main.exe calisiyorsa ne olacak?

Ben olsam direk log derdim.

_ unutulmus.

Sundan cok var, bir fonksiyon mu yazsak?


20 senedir profosyonel olarak kod yazan, bir daha kimsenin gormeyecegi kodun commit mesajina bile dikkat eden birinin yorumlari boyle. Kod kalitesi disinda bir seyi ogrenme veya pratik yapma amacli yazdigin kodu bu standarda tabi tutmak istemeyebilirsin.

5 Beğeni

Moralimi bozmanızın aksine benim de böyle bir şeye ihtiyacımın olduğunu düşünüyorum.

Aslında isminin yeterince açıklayıcı olduğunu düşünüyordum. Fonksiyon kayıt defterinde belirtilen anahtarın var olup olmadığını kontrol ediyor ve ona göre boolean bir değer döndürüyor.

Aslında bunu isimlendirmem pek iyi olmamış herhalde. Çünkü fonksiyonun amacı siteye http isteği atarak kurulum için gereken zip dosyalarının baytını almaktı.

Aslında ilk denediğimde dediğiniz gibi yapmıştım, beceriksizliğimden olacak ki beceremedim. Ancak şu an düzelttim.

Constant değer değil aslında. Belki isimlendirmem const gibi olmuştur bilmiyorum ancak bu değişken kodda da göreceğiniz üzere siteye istek atarken SSL sertifikasını doğrulayıp doğrulamayacağını kontrol ediyor.

Aslında böyle isimlendirmemin sebebi programın hem installer hem de updater olarak kullanılabilmesi. Birbirine karışmasın diye böyle isim koymuştum.

Sizin önerilerinizden hareketle kodu şöyle düzenledim. (Ne diyeyim gözüme daha hoş geldi şimdi.):

from traceback import format_exc
from sys import exit
from requests import Session, exceptions
from utils.process import kill, start, process_list
from utils.env_paths import PROGRAM_PATH, TEMP_PATH, SETUP_PATH
from utils.log import Log
from runtools import registry, server, file, gui
from winreg import HKEY_CURRENT_USER

VERIFY_SSL = True

def start_installer():
    global VERIFY_SSL
    install_log = Log("Installer")
    
    install_log.write("Getting app contents from Github...")
    try:
        content_bytes = server.get_program_contents(timeout=3,verify=VERIFY_SSL)
    except exceptions.SSLError:
        install_log.write(f"An error occured due to SSL certificate authentication:\n{format_exc()}\n")
        
        ask_no_ssl_verify = gui.ask_and_show_error("SSL doğrulaması başarısız oldu. SSL doğrulamasını es geçip gene de devam etmek istiyor musunuz?\n(Bu yöntem güvenlik açıklarına sebep olacağından tavsiye edilmez.)","Player | Kurulum")        
        if ask_no_ssl_verify:
            VERIFY_SSL = False
            start_installer()
        else:
            exit(1)
            
    except exceptions.ConnectTimeout:
        install_log.write(f"Connection timed out:\n{format_exc()}\n")
        
        gui.show_error("Sunucuya gönderilen istek zaman aşımına uğradı.","Player | Kurulum")
        exit(1)
        
    except exceptions.ConnectionError:
        install_log.write(f"An error occured while getting app contents from Github:\n{format_exc()}\n")
        
        gui.show_error("Program internet olmadığından bilgisayara indirilemedi.","Player | Kurulum")
        exit(1)

    try:
        install_log.write(f"Creating temporary folder: \"{SETUP_PATH}\"")
        file.create_dir(SETUP_PATH)

        install_log.write(f"Downloading app contents...")
        file.write_byte(f"{SETUP_PATH}\\executable.zip","wb",content_bytes)
        install_log.write(f"App contents have downloaded.")

        install_log.write(f"Creating contents' folder: \"{PROGRAM_PATH}\"")
        file.create_dir(PROGRAM_PATH)
        install_log.write(f"Contents' folder created: \"{PROGRAM_PATH}\"")

        install_log.write("Extracting contents from temporary zip file...")
        file.extract(f"{SETUP_PATH}\\executable.zip",PROGRAM_PATH)
        install_log.write("Contents have extracted from zip file.")

        install_log.write("Deleting temporary setup folder...")
        file.remove_all(SETUP_PATH)
        install_log.write("Temporary setup folder been deleted.")
            
        install_log.write("Creating registry value at: \"HKEY_CURRENT_USER\\SOFTWARE\\Player\\version\"")
        registry.create_key(HKEY_CURRENT_USER,"SOFTWARE\\Player","version",server.get_version())
        install_log.write("Created registry value at: \"HKEY_CURRENT_USER\\SOFTWARE\\Player\\version\"")

    except PermissionError as exc:
        install_log.write(f"An error occured due to system permissions:\n{format_exc()}\n")
        gui.show_error("Yetki hatası. Programı yönetici olarak çalıştırmayı deneyin.","Player | Kurulum")
        exit(1)

    except Exception as exc:
        exc_tb = format_exc()
        install_log.write(f"An unexpected error occured:\n{exc_tb}\n")
        gui.show_error(0,f"Beklenmedik bir hata oluştu:\n\n{exc_tb}", "Player | Kurulum")
        exit(1)

    else:
        install_log.write("Installation completed without any issues.")

def start_updater():
    global VERIFY_SSL
    updater_log = Log("Updater")
    
    updater_log.write("Getting version information from Github...")
    try:
        local_version = registry.read_key(HKEY_CURRENT_USER,"SOFTWARE\\Player","version")
        main_version = server.get_version()
    except exceptions.SSLError:
        updater_log.write(f"An error occured due to SSL certificate authentication:\n{format_exc()}\n")
        
        ask_no_ssl_verify = gui.ask_and_show_error("SSL doğrulaması başarısız oldu. SSL doğrumasını es geçip gene de devam etmek istiyor musunuz?\n(Bu yöntem güvenlik açıklarına sebep olacağından tavsiye edilmez.)","Player | Güncelleme Sistemi")        
        if ask_no_ssl_verify:
            VERIFY_SSL = False
            start_updater()
        else:
            pass
            
    except Exception:
        updater_log.write(f"An error occured while getting app contents from Github:\n{format_exc()}\n")
    
    else:
        if local_version != main_version:
            updater_log.write("Application isn't up to date.")

            updater_log.write("Getting app contents from Github...")
            try:
                content_bytes = server.get_program_contents(timeout=3,verify=VERIFY_SSL)
                
            except exceptions.ConnectTimeout:
                updater_log.write(f"Connection timed out:\n{format_exc()}\n")
            
                gui.show_error("Sunucuya gönderilen istek zaman aşımına uğradı.","Player | Kurulum")
                exit(1)
            
            except exceptions.ConnectionError:
                updater_log.write(f"An error occured while getting app contents from Github:\n{format_exc()}\n")
            
                gui.show_error("Program internet olmadığından bilgisayara indirilemedi.","Player | Kurulum")
                exit(1)  
            
            
            try:
                if any(b"player_gui.exe" in i for i in process_list()):
                    kill("player_gui.exe")
                    updater_log.write("Killed 'player_gui.exe'")

                        
                updater_log.write(f"Creating temporary folder: \"{TEMP_PATH}\"")
                file.create_dir(TEMP_PATH)

                updater_log.write(f"Downloading app contents...")
                file.write_byte(f"{TEMP_PATH}\\executable.zip","wb",content_bytes)
                updater_log.write(f"App contents have downloaded.")

                updater_log.write("App folder is cleaning...")
                file.remove_all(PROGRAM_PATH)
                file.create_dir(PROGRAM_PATH)
                updater_log.write("App folder been cleaned.")
                         
                updater_log.write("Extracting contents from temporary zip file...")
                file.extract(f"{TEMP_PATH}\\executable.zip",PROGRAM_PATH)
                updater_log.write("Contents have extracted from zip file.")

                updater_log.write("Deleting temporary folder...")
                file.remove_all(TEMP_PATH)
                updater_log.write("Temporary folder been deleted.")
                        
                updater_log.write("Updating registry value at: \"HKEY_CURRENT_USER\\SOFTWARE\\Player\\version\"")
                registry.create_key(HKEY_CURRENT_USER,"SOFTWARE\\Player","version",server.get_version())
                updater_log.write("Updated registry value at: \"HKEY_CURRENT_USER\\SOFTWARE\\Player\\version\"")

            except PermissionError as exc:
                gui.show_error("Yetki hatası. Uygulamayı yönetici olarak çalıştırmayı deneyin.", "Player | Güncelleme Sistemi")
                exit(1)

            except Exception as exc:
                exc_tb = format_exc()
                gui.show_error(f"Beklenmedik bir hata oluştu:\n\n{exc_tb}", "Player | Güncelleme Sistemi")
                exit(1)

            else:
                updater_log.write("Update completed without any issues.")
                        
        elif local_version == main_version:
            updater_log.write("Application is already up to date.")


def main():
    if not registry.check_key(HKEY_CURRENT_USER,"SOFTWARE\\Player","version"):
        start_installer()
        
    else:
        start_updater()

    if not any(b"player_gui.exe" in i for i in process_list()):
        start(f"{PROGRAM_PATH}\\player_gui.exe")

    exit(0)

if __name__ == "__main__":
    main()

genelde kodun içine uyarıları mesajları direk olarak yazmayız dil desteği için:

kullnaiblirsin. Daha profesyonel olur.

mesela şurada bu gibi komutları ay bir dosyada fonksiyona alaiblirsin veya class :

def command_install():
        install_log.write(f"Creating temporary folder: \"{SETUP_PATH}\"")
        file.create_dir(SETUP_PATH)

        install_log.write(f"Downloading app contents...")
        file.write_byte(f"{SETUP_PATH}\\executable.zip","wb",content_bytes)
        install_log.write(f"App contents have downloaded.")

        install_log.write(f"Creating contents' folder: \"{PROGRAM_PATH}\"")
        file.create_dir(PROGRAM_PATH)
        install_log.write(f"Contents' folder created: \"{PROGRAM_PATH}\"")

        install_log.write("Extracting contents from temporary zip file...")
        file.extract(f"{SETUP_PATH}\\executable.zip",PROGRAM_PATH)
        install_log.write("Contents have extracted from zip file.")

        install_log.write("Deleting temporary setup folder...")
        file.remove_all(SETUP_PATH)
        install_log.write("Temporary setup folder been deleted.")
            
        install_log.write("Creating registry value at: \"HKEY_CURRENT_USER\\SOFTWARE\\Player\\version\"")
        registry.create_key(HKEY_CURRENT_USER,"SOFTWARE\\Player","version",server.get_version())
        install_log.write("Created registry value at: \"HKEY_CURRENT_USER\\SOFTWARE\\Player\\version\"")

böylelikle try içinde

try: 
    command_install()
except PermissionError as exc:
   command_permission_error()
except Exception as exc:
   command_exception_error()

gibi daha okunaklı olur. Yzdıkça neyin ayrılacağını, neyin yeni fonksiyon olacağını daha iyi anlarsın.

1 Beğeni

Aslında ben de düşündüm ancak iki ayrı fonksiyona da bir fonksiyonun yerel değişkenini belirtmem gerekeceğinden güzel durmayacağını düşündüm. Ayrıca kod pek bölük pörçük olur diye düşünüyorum. Ayrı dosyaya koymak pek mantıklı olmaz diye sanıyorum (Bu fonksiyonu sadece 1 tane dosya kullanacağı için).

Tavsiyeniz için teşekkürler. Eğer kütüphane çok büyük değilse koda eklerim.

Çok az satır olması kodlarda şirketin sevdiği bir durum önemli olaylardan bir tanesi bu ikinci olarak ise okunabilir anlaşılır olması diğer yazılımcıların rahat okumasida önemli bunlara dikkat etmelesin.

key_exists?
check_key_for_existence?
“check” herhangi bir check olabilir—degerinin 42 olup olmadigi da bir check

kurulum_icin_gereken_zip_dosyalarini_indir_http

Catch (except) clause’lari icin kullanilan sentaks cogu dilde cok basit, zaman zaman asagiya kod olarak dusmesi cok normal.

Iki ayri fonksiyonalitenin iki ayri fonksiyona ayrilmasi icin bir neden daha.


Yeni hali cok daha temiz olmus, ellerine saglik. Biraz daha yorumum var:

Bu degisken hala constant taklidi yapiyor.

Koskoca programin kapanacagina bir start_installer fonksiyonu karar veremez. Installer basarisiz olduysa bunu False veya baska bir hata degeri dondurerek, veya exception throw ederek yukariya belirtir. Bu durumda ne yapilacagi (exit(1)) yukarisinin karari. main’deysek olebiliriz veya return edebiliriz fakat oyun icinde pencereden calisiyorsak mesela butun oyunu oldurmeye gerek yok.

Hatta gui’den gosterilen hata mesajlari da donen hata kodlarina baglanabilir.

Exception ile detayli hata bilgisi dondurmeye ornek:

https://github.com/aib/usb-pcap/blob/master/processor.py#L23

open’a giden "wb" argumani zaten “write byte” anlamina geliyor—bu parametreyi paslamaya gerek yok.

Sanki bu log mesajlari write (create_folder degil mi?) tarafindan da yazilabilir.

Logger’in asagiya paslanmasi cok yaygin bir problem ve kanimca nonlocal context kullanimini (mesela global logger degiskeni veya log fonksiyonu) hakli cikartan sayili durumlardan biri.

Kastiracaksa degmez ama.

Sunu en basa alip up-to-date ise ciksak olmaz mi?
Butun fonksiyon bir indentation level’indan kurtulur.

Peki, siz ne önerirsiniz? Tüm program boyunca bir değişken siteye atılan istekte SSL doğrulamasının yapılıp yapılmayacağını kontrol etmeli ve hata alırsa gelecek tüm isteklerinde SSL doğrulamasını pas geçmesi gerekiyor. Aklıma en yatkın bu çözüm geldi.

Bu create_dir fonksiyonunu farklı yerlerde de kullanabileyim diye log’u programın içinde tutuyorum.

Aslında program iki yol ayrımından birine (Installer’a girmek veya güncellemek) girdiği vakit diğer yolu seçmiyor zaten. Seçmediği için de başka bir işlem yapması da muhtemel değil. Sizin koskoca programı küçük bir fonksiyonda kapatma kastınız bu galiba. Ancak programın kendisi bu iki fonksiyondan biri zaten. Ana fonksiyon sadece kontrol edip diğer iki fonksiyonu çalıştırıyor o kadar. Ayrıca programı fonksiyon içerisinde hata oluştuğu gibi kapatmayı daha mantıklı bulmuştum. Ancak ana fonksiyonda kapatılır mı derseniz, kapatılır pek fark etmez.

Dediğiniz saçma kısımları düzeltip (ayrıca main’de kapatma kısmını biraz saçma bulsam da) kodu şöyle bir hale getirdim:

from traceback import format_exc
from sys import exit
from requests import Session, exceptions
from utils.exceptions import ErrorOccuredException
from utils.process import kill, start, process_list
from utils.env_paths import PROGRAM_PATH, TEMP_PATH, SETUP_PATH
from utils.log import Log
from runtools import registry, server, file, gui
from winreg import HKEY_CURRENT_USER

VERIFY_SSL = True

def start_installer():
    global VERIFY_SSL
    install_log = Log("Installer")
    
    install_log.write("Getting app contents from Github...")
    try:
        content_bytes = server.get_program_contents(timeout=3,verify=VERIFY_SSL)
    except exceptions.SSLError:
        install_log.write(f"An error occured due to SSL certificate authentication:\n{format_exc()}\n")
        
        ask_no_ssl_verify = gui.ask_and_show_error("SSL doğrulaması başarısız oldu. SSL doğrulamasını es geçip gene de devam etmek istiyor musunuz?\n(Bu yöntem güvenlik açıklarına sebep olacağından tavsiye edilmez.)","Player | Kurulum")        
        if ask_no_ssl_verify:
            VERIFY_SSL = False
            start_installer()
        else:
            raise ErrorOccuredException
            
    except exceptions.ConnectTimeout:
        install_log.write(f"Connection timed out:\n{format_exc()}\n")
        
        gui.show_error("Sunucuya gönderilen istek zaman aşımına uğradı.","Player | Kurulum")
        raise ErrorOccuredException
        
    except exceptions.ConnectionError:
        install_log.write(f"An error occured while getting app contents from Github:\n{format_exc()}\n")
        
        gui.show_error("Program internet olmadığından bilgisayara indirilemedi.","Player | Kurulum")
        raise ErrorOccuredException

    try:
        install_log.write(f"Creating temporary folder: \"{SETUP_PATH}\"")
        file.create_dir(SETUP_PATH)

        install_log.write(f"Downloading app contents...")
        file.write_byte(f"{SETUP_PATH}\\executable.zip",content_bytes)
        install_log.write(f"App contents have downloaded.")

        install_log.write(f"Creating contents' folder: \"{PROGRAM_PATH}\"")
        file.create_dir(PROGRAM_PATH)
        install_log.write(f"Contents' folder created: \"{PROGRAM_PATH}\"")

        install_log.write("Extracting contents from temporary zip file...")
        file.extract(f"{SETUP_PATH}\\executable.zip",PROGRAM_PATH)
        install_log.write("Contents have extracted from zip file.")

        install_log.write("Deleting temporary setup folder...")
        file.remove_all(SETUP_PATH)
        install_log.write("Temporary setup folder been deleted.")
            
        install_log.write("Creating registry value at: \"HKEY_CURRENT_USER\\SOFTWARE\\Player\\version\"")
        registry.create_key(HKEY_CURRENT_USER,"SOFTWARE\\Player","version",server.get_version())
        install_log.write("Created registry value at: \"HKEY_CURRENT_USER\\SOFTWARE\\Player\\version\"")

    except PermissionError as exc:
        install_log.write(f"An error occured due to system permissions:\n{format_exc()}\n")
        gui.show_error("Yetki hatası. Programı yönetici olarak çalıştırmayı deneyin.","Player | Kurulum")
        return 1

    except Exception as exc:
        exc_tb = format_exc()
        install_log.write(f"An unexpected error occured:\n{exc_tb}\n")
        gui.show_error(0,f"Beklenmedik bir hata oluştu:\n\n{exc_tb}", "Player | Kurulum")
        raise ErrorOccuredException

    else:
        install_log.write("Installation completed without any issues.")
        

def start_updater():
    global VERIFY_SSL
    updater_log = Log("Updater")
    
    updater_log.write("Getting version information from Github...")
    try:
        local_version = registry.read_key(HKEY_CURRENT_USER,"SOFTWARE\\Player","version")
        main_version = server.get_version()
    except exceptions.SSLError:
        updater_log.write(f"An error occured due to SSL certificate authentication:\n{format_exc()}\n")
        
        ask_no_ssl_verify = gui.ask_and_show_error("SSL doğrulaması başarısız oldu. SSL doğrumasını es geçip gene de devam etmek istiyor musunuz?\n(Bu yöntem güvenlik açıklarına sebep olacağından tavsiye edilmez.)","Player | Güncelleme Sistemi")        
        if ask_no_ssl_verify:
            VERIFY_SSL = False
            start_updater()
        else:
            pass
            
    except Exception:
        updater_log.write(f"An error occured while getting app contents from Github:\n{format_exc()}\n")
    
    else:
        if local_version == main_version:
            updater_log.write("Application is already up to date.")
        
        elif local_version != main_version:
            updater_log.write("Application isn't up to date.")

            updater_log.write("Getting app contents from Github...")
            try:
                content_bytes = server.get_program_contents(timeout=3,verify=VERIFY_SSL)
                
            except exceptions.ConnectTimeout:
                updater_log.write(f"Connection timed out:\n{format_exc()}\n")
            
                gui.show_error("Sunucuya gönderilen istek zaman aşımına uğradı.","Player | Kurulum")
                raise ErrorOccuredException
            
            except exceptions.ConnectionError:
                updater_log.write(f"An error occured while getting app contents from Github:\n{format_exc()}\n")
            
                gui.show_error("Program internet olmadığından bilgisayara indirilemedi.","Player | Kurulum")
                raise ErrorOccuredException
            
            
            try:
                if any(b"player_gui.exe.exe" in i for i in process_list()):
                    kill("player_gui.exe.exe")
                    updater_log.write("Killed 'player_gui.exe'")

                        
                updater_log.write(f"Creating temporary folder: \"{TEMP_PATH}\"")
                file.create_dir(TEMP_PATH)

                updater_log.write(f"Downloading app contents...")
                file.write_byte(f"{TEMP_PATH}\\executable.zip",content_bytes)
                updater_log.write(f"App contents have downloaded.")

                updater_log.write("App folder is cleaning...")
                file.remove_all(PROGRAM_PATH)
                file.create_dir(PROGRAM_PATH)
                updater_log.write("App folder been cleaned.")
                         
                updater_log.write("Extracting contents from temporary zip file...")
                file.extract(f"{TEMP_PATH}\\executable.zip",PROGRAM_PATH)
                updater_log.write("Contents have extracted from zip file.")

                updater_log.write("Deleting temporary folder...")
                file.remove_all(TEMP_PATH)
                updater_log.write("Temporary folder been deleted.")
                        
                updater_log.write("Updating registry value at: \"HKEY_CURRENT_USER\\SOFTWARE\\Player\\version\"")
                registry.create_key(HKEY_CURRENT_USER,"SOFTWARE\\Player","version",server.get_version())
                updater_log.write("Updated registry value at: \"HKEY_CURRENT_USER\\SOFTWARE\\Player\\version\"")

            except PermissionError as exc:
                gui.show_error("Yetki hatası. Uygulamayı yönetici olarak çalıştırmayı deneyin.", "Player | Güncelleme Sistemi")
                raise ErrorOccuredException

            except Exception as exc:
                exc_tb = format_exc()
                gui.show_error(f"Beklenmedik bir hata oluştu:\n\n{exc_tb}", "Player | Güncelleme Sistemi")
                raise ErrorOccuredException

            else:
                updater_log.write("Update completed without any issues.")
                raise ErrorOccuredException
                        


def main():
    if not registry.check_key_exists(HKEY_CURRENT_USER,"SOFTWARE\\Player","version"):
        try:
            start_installer()
        except ErrorOccuredException:
            exit(1)
    else:
        try:
            start_updater()
        except ErrorOccuredException:
            exit(1)
    
    if not any(b"player_gui.exe" in i for i in process_list()):
        start(f"{PROGRAM_PATH}\\player_gui.exe")

    
    exit(0)

if __name__ == "__main__":
    main()
        

    
    


verify_ssl

Log mesajini daha jenerik yapmak (“Creating {folder}”) da bir opsiyon.

Ana fonksiyondan donmeyecek fonksiyonlar cagirmak garip. Daha dogrusu,

daha dogrusu, hata oldugunda donmeyen fonksiyonlar icin bicilmis kaftan exception’lar. Isteyen yakalayip yoluna devam edebiliyor, isteyen hic bir sey yazmadan “hata oldugunda kapan” mantigina default edebiliyor.

Burada return edersek asagida elif’e ihtiyac yok; kodun gorsel karmasikligi azaliyor.

PROGRAM_NAME = 'player_gui.exe'
GET_PROGRAM_CONTENTS_TIMEOUT = 3
VERSION_REGISTRY_PATH = 'SOFTWARE\\Player'
VERSION_REGISTRY_KEY = 'version'

def try_with_ssl(action):
    while True:
        try:
            return action(True)
        except exceptions.SSLError:
            logger.write(f"An error occured due to SSL certificate authentication:\n{format_exc()}\n")

            ask_no_ssl_verify = gui.ask_and_show_error("SSL doğrulaması başarısız oldu. SSL doğrulamasını es geçip gene de devam etmek istiyor musunuz?\n(Bu yöntem güvenlik açıklarına sebep olacağından tavsiye edilmez.)","Player | Kurulum")
            if ask_no_ssl_verify:
                return action(False)
            else:
                raise

def get_app_contents2(logger):
    logger.write("Getting app contents from Github...")
    def _get(verify_ssl):
        return server.get_program_contents(timeout=GET_PROGRAM_CONTENTS_TIMEOUT, verify=verify_ssl)
    return try_with_ssl(_get)

def get_app_contents1(logger):
    verify_ssl = True

    while True:
        logger.write("Getting app contents from Github...")
        try:
            content_bytes = server.get_program_contents(timeout=GET_PROGRAM_CONTENTS_TIMEOUT, verify=verify_ssl)
            return content_bytes
        except exceptions.SSLError:
            logger.write(f"An error occured due to SSL certificate authentication:\n{format_exc()}\n")

            ask_no_ssl_verify = gui.ask_and_show_error("SSL doğrulaması başarısız oldu. SSL doğrulamasını es geçip gene de devam etmek istiyor musunuz?\n(Bu yöntem güvenlik açıklarına sebep olacağından tavsiye edilmez.)","Player | Kurulum")
            if ask_no_ssl_verify:
                verify_ssl = False
                continue
            else:
                raise

def start_installer():
    logger = Log("Installer")

    get_app_contents(logger)

    file.set_logger(logger)
    file.create_dir(SETUP_PATH)
    file.write_byte(f"{SETUP_PATH}\\executable.zip",content_bytes)
    file.create_dir(PROGRAM_PATH)
    file.extract(f"{SETUP_PATH}\\executable.zip",PROGRAM_PATH)
    file.remove_all(SETUP_PATH)

    registry.set_logger(logger)
    registry.create_key_with_value(HKEY_CURRENT_USER, VERSION_REGISTRY_PATH, VERSION_REGISTRY_KEY, server.get_version())

    logger.write("Installation completed without any issues.")

def start_updater():
    logger = Log("Updater")

    verify_ssl = True

    updater_log.write("Getting version information from Github...")
    def read_versions(verify_ssl):
        local_version = registry.read_key(HKEY_CURRENT_USER,VERSION_REGISTRY_PATH,VERSION_REGISTRY_KEY)
        main_version = server.get_version(verify_ssl)
        return (local_version, main_version)
    local_version, main_version = try_with_ssl(read_versions)

    if local_version == main_version:
        logger.write("Application is up-to-date.")
        return

    content_bytes = get_app_contents(logger)

    if any(bytes(PROGRAM_NAME) in i for i in process_list()):
        kill(PROGRAM_NAME)
        logger.write(f"Killed '{PROGRAM_NAME}'")

    file.set_logger(logger)
    file.create_dir(TEMP_PATH)
    file.write_byte(f"{TEMP_PATH}\\executable.zip",content_bytes)
    file.remove_all(PROGRAM_PATH)
    file.create_dir(PROGRAM_PATH)
    file.extract(f"{TEMP_PATH}\\executable.zip",PROGRAM_PATH)
    file.remove_all(TEMP_PATH)

    registry.set_logger(logger)
    registry.create_key(HKEY_CURRENT_USER,VERSION_REGISTRY_PATH,VERSION_REGISTRY_KEY,server.get_version())

def main():
    if not registry.key_exists(HKEY_CURRENT_USER,VERSION_REGISTRY_PATH,VERSION_REGISTRY_KEY):
        try:
            start_installer()
        except exceptions.SSLError: #ikinci kere
            logger.write(f"An error occured due to SSL certificate authentication:\n{format_exc()}\n")
            exit(1)
        except exceptions.ConnectTimeout:
            logger.write(f"Connection timed out:\n{format_exc()}\n")
            gui.show_error("Sunucuya gönderilen istek zaman aşımına uğradı.","Player | Kurulum")
            exit(1)
        except exceptions.ConnectionError:
            logger.write(f"An error occured while getting app contents from Github:\n{format_exc()}\n")
            gui.show_error("Program internet olmadığından bilgisayara indirilemedi.","Player | Kurulum")
            exit(1)
        except PermissionError as exc:
            logger.write(f"An error occured due to system permissions:\n{format_exc()}\n")
            gui.show_error("Yetki hatası. Programı yönetici olarak çalıştırmayı deneyin.","Player | Kurulum")
            exit(1)
        except Exception as exc:
            exc_tb = format_exc()
            logger.write(f"An unexpected error occured:\n{exc_tb}\n")
            gui.show_error(0,f"Beklenmedik bir hata oluştu:\n\n{exc_tb}", "Player | Kurulum")
            exit(1)

    else:
        try:
            start_updater()
        except PermissionError as exc:
            gui.show_error("Yetki hatası. Uygulamayı yönetici olarak çalıştırmayı deneyin.", "Player | Güncelleme Sistemi")
            exit(1)
        except Exception as exc:
            exc_tb = format_exc()
            gui.show_error(f"Beklenmedik bir hata oluştu:\n\n{exc_tb}", "Player | Güncelleme Sistemi")
            exit(1)

    if not any(bytes(PROGRAM_NAME) in i for i in process_list()):
        start(os.path.join(PROGRAM_PATH, PROGRAM_NAME))

    exit(0)

if __name__ == "__main__":
    main()

Dürüst olayım, bana kendi yazdığımdan daha karmaşık geldi. Ayrıca bazı konulara açıklık getireyim.

Runtoolsdan modül import ediyorum, class import etmiyorum. Etsem de class’a logger eklesek dahi statik metodlarda pek kullanışlı olacağını sanmam.

Log sınıfı ise instance’I oluşturulduğunda gönderdiğimiz argüman adında bir log dosyası oluşturuyor. write metodunu da tahmin edebiliyorsunuzdur zaten.

Ayrıca try_with_ssl fonksiyonunu bir nevi decorator gibi kullanmak bize ne katıyor anlamadım.

Buradaki kodun avantajını ya da dezavantajını söylerseniz müteşekkir olurum.

Olur, neden olmasin.

Ama zaten bu is icin logging kutuphanelerini kullaniyoruz. En buyuk sorun olan logger context’ine herhangi bir yerden erismeyi cozuyorlar. Burada modulleri adapte ettim, aklima gelen en kolay sekilde.

Kodda tam uc kere tekrarlanan “is yap; SSL hatasi alirsan soru sor; pozitif cevap alirsan SSL’siz tekrar dene” kismini fonksiyona aliyor. Standart DRY prensibi. (DRY’in kattigi her seyi katiyor.)

Ornek kodda bir tanesini biraktim (get_app_contents1), optimizasyonun bir onceki adimi gorulebilsin diye. Normalde onu silip 2’yi kullaniyoruz.

TLS (“SSL”) guvenligini baypas etmenin cok riskli olduguna karar verdik ve sorunun sadece bazi sartlarda cikmasini istedik. Veya toplamda sadece bir kere sorulmasini. Veya soru metninin degistirilmesini. Orijinal kodda kac tane degisiklik yapacagiz, yeni kodda kac tane.

Daha da onemlisi, orijinal kodda bir yeri degistirmeyi unutma ihtimalimiz nedir, yeni kodda nedir? Iste bu yuzden DRY.

Cunku karmasikligi baska yerlerde goruyoruz. Benim start_installer’a bir bakista yaptigi islemlerin tamamini gorebiliyoruz. Islemlerin detayina indigimizde fonksiyon alan fonksiyonlar, error handling detaylari ortaya cikiyor.

Orijinal kodu satir satir okumak daha basit, fakat kodun ne yaptigini anlayabilmek icin satir satir okumak mecburiyetindeyiz. Calisan “happy path” ile hata idare eden yollar, retry mantiklari icice. Exception handler’i icindeyken bir anda installer baslatiyoruz.

Ama normal tabi. Ilk yanitta demistim.

hocam anladığım kadarıyla sorun sadece “yazım” kalitesi. python kullanıyorsan vscode’da pylint eklentisi baya iş görüyor. onun dışında flake8 kullanabilirsin.

Hafta içi müsait olamadığımdan şimdi yazıyorum.

Programın SSL ile siteye istek atması kısmında bir dezavantaj göremiyorum (Recursion hatası dışında) ve programda istek atma kısmı farklı şekilde farklı şekillerde kullanımlarda farklar olduğu için o kısma ellemeyeceğim (Fikrimi değiştirdim, siteden baytları çekme kısmını yeniden düzenledim.). Ancak programın dosyalarını çıkartıp kurma kısmını install_program_contents adlı bir fonksiyon altında topladım.

from traceback import format_exc
from sys import exit
from requests import Session, exceptions
from utils.process import kill, start, process_list
from utils.env_paths import PROGRAM_PATH, TEMP_PATH, FULL_KEY_PATH, KEY_PATH, KEY_NAME
from utils.log import Log
from runtools import registry, server, file, gui
from winreg import HKEY_CURRENT_USER
from time import sleep

verify_ssl = True
log = None  

def download_program_contents(mode):
    global verify_ssl
    
    log.write("Getting app contents from Github...")
    try:
        return (0, server.get_program_contents(timeout=3,verify=verify_ssl))
    except exceptions.SSLError:
        log.write(f"An error occured due to SSL certificate authentication:\n{format_exc()}\n")
        
        ask_no_ssl_verify = gui.ask_and_show_error("SSL doğrulaması başarısız oldu. SSL doğrulamasını es geçip gene de devam etmek istiyor musunuz?\n(Bu yöntem güvenlik açıklarına sebep olacağından tavsiye edilmez.)","Player | Kurulum")        
        if ask_no_ssl_verify:
           verify_ssl = False
           if mode == "install":
               start_installer()
           elif mode == "update":
               start_updater()
               
        else:
            if mode == "install":
                log.close()
                return (1, None)
            
    except exceptions.ConnectTimeout:
        if mode == "install":
            log.write(f"Connection timed out:\n{format_exc()}\n")
            
            gui.show_error("Sunucuya gönderilen istek zaman aşımına uğradı.","Player | Kurulum")
            log.close()
            return (1, None)
        
    except exceptions.ConnectionError:
        if mode == "install":
            log.write(f"An error occured while getting app contents from Github:\n{format_exc()}\n")
            
            gui.show_error("Program internet olmadığından bilgisayara indirilemedi.","Player | Kurulum")
            log.close()
            return (1, None)

def install_program_contents(content_bytes,mode):
    try:
        log.write(f"Creating temporary folder: \"{TEMP_PATH}\"")
        file.create_dir(TEMP_PATH)

        log.write(f"Downloading app contents...")
        file.write_byte(f"{TEMP_PATH}\\executable.zip",content_bytes)
        log.write(f"App contents have downloaded.")

        if mode == "update":
            log.write("App folder is cleaning...")
            file.remove_all(PROGRAM_PATH)
            file.create_dir(PROGRAM_PATH)
            log.write("App folder been cleaned.")

        elif mode == "install":
            log.write(f"Creating app folder: \"{PROGRAM_PATH}\"")
            file.create_dir(PROGRAM_PATH)
            log.write(f"App folder created: \"{PROGRAM_PATH}\"")

        log.write("Extracting contents from temporary zip file...")
        file.extract(f"{TEMP_PATH}\\executable.zip",PROGRAM_PATH)
        log.write("Contents have extracted from zip file.")

        log.write("Deleting temporary setup folder...")
        file.remove_all(TEMP_PATH)
        log.write("Temporary setup folder been deleted.")
            
        if mode == "update":
            log.write(f"Updating registry value at: \"{FULL_KEY_PATH}\"")
            registry.create_key(HKEY_CURRENT_USER,KEY_PATH,KEY_NAME,server.get_version())
            log.write(f"Updated registry value at: \"{FULL_KEY_PATH}\"")

        elif mode == "install":
            log.write(f"Creating registry value at: \"{FULL_KEY_PATH}\"")
            registry.create_key(HKEY_CURRENT_USER,KEY_PATH,KEY_NAME,server.get_version())
            log.write(f"Created registry value at: \"{FULL_KEY_PATH}\"")

    except PermissionError as exc:
        log.write(f"An error occured due to system permissions:\n{format_exc()}\n")
        gui.show_error("Yetki hatası. Programı yönetici olarak çalıştırmayı deneyin.",f"Player | {'Kurulum' if mode == 'install' else 'Güncelleme Sistemi'}")
        log.close()
        return 1
        
    except Exception as exc:
        exc_tb = format_exc()
        log.write(f"An unexpected error occured:\n{exc_tb}\n")
        gui.show_error(f"Beklenmedik bir hata oluştu:\n\n{exc_tb}",f"Player | {'Kurulum' if mode == 'install' else 'Güncelleme Sistemi'}")
        log.close()
        return 1

    else:
        log.write(f"{'Installation' if mode == 'install' else 'Update'} completed without any issues.")
        log.close()




def start_installer():
    global log
    log.start()
    
    error_occured, content_bytes = download_program_contents(mode="install")
    if error_occured:
        exit(1)
    
    if error_occured := install_program_contents(content_bytes,mode="install"):
        exit(1)

    
def start_updater():
    global log
    log.start()

    log.write(f"Reading registry value at: \"{FULL_KEY_PATH}\"")
    local_version = registry.read_key(HKEY_CURRENT_USER,KEY_PATH,KEY_NAME)
    log.write(f"Read registry value at: \"{FULL_KEY_PATH}\"")
    
    log.write("Getting version information from Github...")
    try:
        main_version = server.get_version()
    except exceptions.SSLError:
        log.write(f"An error occured due to SSL certificate authentication:\n{format_exc()}\n")
        
        ask_no_ssl_verify = gui.ask_and_show_error("SSL doğrulaması başarısız oldu. SSL doğrumasını es geçip gene de devam etmek istiyor musunuz?\n(Bu yöntem güvenlik açıklarına sebep olacağından tavsiye edilmez.)","Player | Güncelleme Sistemi")        
        if ask_no_ssl_verify:
            verify_ssl = False
            start_updater()
        else:
            pass
            
    except Exception:
        log.write(f"An error occured while getting app contents from Github:\n{format_exc()}\n")
    
    else:
        if local_version == main_version:
            log.write("Application is already up to date.")
            log.close()
        
        elif local_version != main_version:
            log.write("Application isn't up to date.")

            error_occured, content_bytes = download_program_contents(mode="update")
            if error_occured:
                exit(1)

            
            if any(b"oba_gui.exe" in i for i in process_list()):
                kill("oba_gui.exe")
                log.write("Killed 'oba_gui.exe'")
                sleep(3)
                 
            if error_occured := install_program_contents(content_bytes,mode="update"):
                exit(1)


def main():
    global log
    
    if not registry.check_key_exists(HKEY_CURRENT_USER,KEY_PATH,KEY_NAME):
        log = Log("Installer")
        start_installer()
    else:
        log = Log("Updater")
        start_updater()
    
    start(f"{PROGRAM_PATH}\\oba_gui.exe")

    log.close()
    exit(0)

if __name__ == "__main__":
    main()