Merhaba, haydi oynayalım! Dövüşçünüzü seçin (taş, kağıt, makas): kaya
Birçok günlük yazılım bir CLI programı olarak paketlenmiştir. Örneğin, herhangi bir UNIX sistemiyle birlikte gönderilen ve terminalde vim <FILE>
çalıştırılarak etkinleştirilebilen bir araç olan vim
metin düzenleyicisini ele alalım.
Örneğin çekirdek bölümde project
özelliğini ayarlamak istediğimizde gcloud config set project <PROJECT_ID>
çalıştırıyoruz.
Argüman | İçerik |
---|---|
Arg 0 | gcloud |
Argüman 1 | yapılandırma |
… | … |
Önceki örneğe göre, çekirdek bölümde gcloud config set project <PROJECT_ID>
çalıştırarak project
özelliğini ayarladık.
Başka bir deyişle set
bir komuttur.
Resmi belgelerinde belirtildiği gibi, gcloud config
komutuna geri dönersek, gcloud config
özellikleri değiştirmenize izin veren bir komut grubudur. Kullanımı şu şekilde:
gcloud config GROUP | COMMAND [GCLOUD_WIDE_FLAG … ]
bu sayede COMMAND set
, list
vb. olabilir... (GROUP'un config
olduğunu unutmayın)
gcloud config
komut grubunun kullanımına dönersek, bu durumda seçenek(ler) GCLOUD_WIDE_FLAG
.
Örneğin komutun detaylı kullanımını ve açıklamasını görüntülemek istediğimizi varsayalım, gcloud config set –help
çalıştırıyoruz. Başka bir deyişle --help
seçenektir.
Diğer bir örnek ise belirli bir projenin hesaplama bölümünde bölge özelliğini ayarlamak istediğimizde gcloud config set compute <ZONE_NAME> –project=<PROJECT_ID>
çalıştırmamızdır. Başka bir deyişle --project
, <PROJECT_ID>
değerini tutan bir seçenektir.
Örneğin, bir dataproc kümesi oluşturmak istediğimizde, gcloud dataproc clusters create <CLUSTER_NAME> –region=<REGION>
çalıştırıyoruz. Ve kullanım belgelerinde belirtildiği gibi:
gcloud dataproc clusters create (CLUSTER: –region=REGION)
--region
bayrağı daha önce yapılandırılmamışsa zorunludur.
Kısa seçenekler -
ile başlar ve ardından tek bir alfasayısal karakter gelir; uzun seçenekler ise --
ile başlar ve ardından birden fazla karakter gelir. Kısa seçenekleri, kullanıcı ne istediğinden emin olduğunda kısayollar olarak düşünün; uzun seçenekler ise daha okunaklıdır.
Rock'ı seçtin! Bilgisayar şimdi seçimini yapacak.
Ekibiniz projedeki sorunları ve ilerlemeyi takip etmek için Trello'yu kullanıyor. Ekibiniz panoyla etkileşim kurmanın daha basitleştirilmiş bir yolunu arıyor; terminal aracılığıyla yeni bir GitHub deposu oluşturmaya benzer bir şey. Ekip, panonun 'Yapılacaklar' sütununa yeni bir kart ekleyebilme temel gereksinimiyle bir CLI programı oluşturmanız için size başvurdu.
İşlevsel gereksinimler
İşlevsel Olmayan Gereksinimler
İsteğe Bağlı Gereksinimler
Not: Son iki sütun hakkında endişelenmeyin, bunu daha sonra öğreneceğiz…
Birim Testleri
Trello
CLI
Yardımcı Araçlar (Çeşitli)
Bölüm 1
py-trello
iş mantığının uygulanmasıBölüm 2
Bölüm 3
Bilgisayar makası seçti! Bakalım bu savaşı kim kazanacak…
trellocli/ __init__.py __main__.py models.py cli.py trelloservice.py tests/ test_cli.py test_trelloservice.py README.md pyproject.toml .env .gitignore
trellocli
: kullanıcılar tarafından kullanılacak paket adı görevi görür; örneğin pip install trellocli
__init__.py
: paketin kökünü temsil eder, klasörü bir Python paketi olarak uygun hale getirir__main__.py
: giriş noktasını tanımlar ve kullanıcıların -m
bayrağını kullanarak dosya yolunu belirtmeden modülleri çalıştırmasına olanak tanır; örneğin python -m <module_name>
yerine python -m <parent_folder>/<module_name>.py
models.py
: küresel olarak kullanılan sınıfları saklar; örneğin, API yanıtlarının uyması beklenen modellercli.py
: CLI komutları ve seçenekleri için iş mantığını saklartrelloservice.py
: py-trello
ile etkileşime geçmek için iş mantığını saklartests
: programın birim testlerini saklartest_cli.py
: CLI uygulaması için birim testlerini saklartest_trelloservice.py
: py-trello
ile etkileşim için birim testlerini saklarREADME.md
: programın belgelerini saklarpyproject.toml
: paketin yapılandırmalarını ve gereksinimlerini saklar.env
: ortam değişkenlerini saklar.gitignore
: sürüm kontrolü sırasında göz ardı edilecek (izlenmeyecek) dosyaları belirtir
Uygulama adı ve sürümü gibi paket sabitlerinin ve değişkenlerin depolandığı yer olan paketimizdeki __init__.py
dosyasıyla başlayarak. Bizim durumumuzda aşağıdakileri başlatmak istiyoruz:
# trellocli/__init__.py __app_name__ = "trellocli" __version__ = "0.1.0" ( SUCCESS, TRELLO_WRITE_ERROR, TRELLO_READ_ERROR ) = range(3) ERRORS = { TRELLO_WRITE_ERROR: "Error when writing to Trello", TRELLO_READ_ERROR: "Error when reading from Trello" }
__main__.py
dosyasına geçtiğinizde programınızın ana akışı burada saklanmalıdır. Bizim durumumuzda, cli.py
dosyasında çağrılabilir bir fonksiyonun olacağını varsayarak CLI program giriş noktasını saklayacağız.
# trellocli/__main__.py from trellocli import cli def main(): # we'll modify this later - after the implementation of `cli.py` pass if __name__ == "__main__": main()
Artık paket kurulduğuna göre README.md
dosyamızı (ana belgeler) güncellemeye bir göz atalım. Takip etmemiz gereken belirli bir yapı yoktur ancak iyi bir README aşağıdakilerden oluşacaktır:
<!--- README.md --> # Overview # Getting Started # Usage # Architecture ## Data Flow ## Tech Stack # Running Tests # Next Steps # References
# pyproject.toml [project] name = "trellocli_<YOUR_USERNAME>" version = "0.1.0" authors = [ { name = "<YOUR_NAME>", email = "<YOUR_EMAIL>" } ] description = "Program to modify your Trello boards from your computer's command line" readme = "README.md" requires-python = ">=3.7" classifiers = [ "Programming Language :: Python :: 3", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", ] dependencies = [] [project.urls] "Homepage" = ""
Listede bir sonraki adım, API sırları ve anahtarları gibi ortam değişkenlerimizi sakladığımız .env
dosyamız olacaktır. Bu dosyanın hassas bilgiler içerdiğinden Git tarafından takip edilmemesi gerektiğini unutmamak önemlidir.
Bizim durumumuzda Trello kimlik bilgilerimizi burada saklayacağız. Trello'da Güçlendirme oluşturmak için izleyin. Daha spesifik olarak, py-trello
kullanımına bağlı olarak, uygulamamız için OAuth'u kullanmayı planladığımızdan Trello ile etkileşim kurmak için aşağıdakilere ihtiyacımız olacak:
API Anahtarınızı ve Sırrınızı aldıktan sonra bunları .env
dosyasında bu şekilde saklayın.
# .env TRELLO_API_KEY=<your_api_key> TRELLO_API_SECRET=<your_api_secret>
Son olarak, bulunabilecek Python .gitignore
şablonunu kullanalım. Bunun, .env
dosyamızın hiçbir zaman takip edilmemesini sağlamak için çok önemli olduğunu unutmayın; eğer bir noktada .env
dosyamız takip edildiyse, daha sonraki adımlarda dosyayı kaldırsak bile, hasar meydana gelir ve kötü niyetli aktörler önceki dosyanın izini sürebilir. hassas bilgiler için yamalar.
Artık kurulum tamamlandığına göre değişikliklerimizi GitHub'a aktaralım. pyproject.toml
dosyasında belirtilen meta verilere bağlı olarak LİSANS'ınızı ve ana sayfa URL'nizi buna göre güncellemeyi unutmayın. Daha iyi taahhütlerin nasıl yazılacağına dair referans için:
İşlevsel gereksinimlere bağlı olarak asıl amacımız kullanıcıların yeni bir kart eklemesine izin vermektir. py-trello
yönteme başvurulması: . Bunu yapabilmek için Board
sınıfından get_list
fonksiyonundan alınabilen List
sınıfından add_card
metodunu çağırmalıyız…
Koda dalmadan önce, pyproject.toml
dosyamızı birim testlerini yazmak/çalıştırmak için gereken bağımlılıkları içerecek şekilde değiştirelim.
# pyproject.toml [project] dependencies = [ "pytest==7.4.0", "pytest-mock==3.11.1" ]
Daha sonra virtualenv'imizi etkinleştirelim ve pip install .
bağımlılıkları yüklemek için.
# tests/test_trelloservice.py # module imports from trellocli import SUCCESS from trellocli.trelloservice import TrelloService from trellocli.models import * # dependencies imports # misc imports def test_get_access_token(mocker): """Test to check success retrieval of user's access token""" mock_res = GetOAuthTokenResponse( token="test", token_secret="test", status_code=SUCCESS ) mocker.patch( "trellocli.trelloservice.TrelloService.get_user_oauth_token", return_value=mock_res ) trellojob = TrelloService() res = trellojob.get_user_oauth_token() assert res.status_code == SUCCESS
Örnek kodumda GetOAuthTokenResponse
models.py
henüz ayarlanmamış bir model olduğuna dikkat edin. Daha temiz kod yazmaya yapı sağlar, bunu daha sonra çalışırken göreceğiz.
Testlerimizi çalıştırmak için python -m pytest
çalıştırmanız yeterlidir. Testlerimizin nasıl başarısız olacağına dikkat edin, ama sorun değil; sonunda işe yarayacak.
Mücadele Köşesi 💡 Kendi başınıza daha fazla test yazmayı deneyebilir misiniz? Testlerimin neye benzediğini görmek için başvurmaktan çekinmeyin
Şimdilik trelloservice
oluşturalım. Yeni bir bağımlılık eklemekle başlayalım, bu py-trello
sarmalayıcısıdır.
# pyproject.toml dependencies = [ "pytest==7.4.0", "pytest-mock==3.11.1", "py-trello==0.19.0" ]
Bir kez daha pip install .
bağımlılıkları yüklemek için.
Şimdi trelloservice
beklediğimiz yanıtları düzenlemek için modellerimizi oluşturmaya başlayalım. Bu kısım için, bekleyebileceğimiz dönüş değeri türünü anlamak için birim testlerimize ve py-trello
kaynak koduna başvurmak en iyisidir.
Örneğin, py-trello
create_oauth_token
işlevine ( ) atıfta bulunarak kullanıcının erişim belirtecini almak istediğimizi varsayalım, dönüş değerinin buna benzer bir şey olmasını beklediğimizi biliyoruz
# trellocli/models.py # module imports # dependencies imports # misc imports from typing import NamedTuple class GetOAuthTokenResponse(NamedTuple): token: str token_secret: str status_code: int
Öte yandan, birbiriyle çelişen adlandırma kurallarına dikkat edin. Örneğin py-trello
modülünün List
adında bir sınıfı vardır. Bunun geçici çözümü, içe aktarma sırasında bir takma ad sağlamak olabilir.
# trellocli/models.py # dependencies imports from trello import List as Trellolist
# trellocli/models.py class GetBoardName(NamedTuple): """Model to store board id Attributes id (str): Extracted board id from Board value type """ id: str
Challenge Köşesi 💡 Kendi başınıza daha fazla model yazmayı deneyebilir misiniz? Modellerimin neye benzediğini görmek için başvurmaktan çekinmeyin
Modeller aşağı, resmi olarak trelloservice
kodlamaya başlayalım. Yine oluşturduğumuz birim testlere başvurmalıyız - mevcut test listesinin hizmet için tam kapsamı sağlamadığını söyleyin, gerektiğinde her zaman geri dönün ve daha fazla test ekleyin.
Her zamanki gibi, tüm içe aktarma ifadelerini en üste ekleyin. Ardından beklendiği gibi TrelloService
sınıfını ve yer tutucu yöntemlerini oluşturun. Fikir şu ki, cli.py
hizmetin paylaşılan bir örneğini başlatacağız ve yöntemlerini buna göre çağıracağız. Ayrıca ölçeklenebilirliği, dolayısıyla geniş kapsama ihtiyacını hedefliyoruz.
# trellocli/trelloservice.py # module imports from trellocli import TRELLO_READ_ERROR, TRELLO_WRITE_ERROR, SUCCESS from trellocli.models import * # dependencies imports from trello import TrelloClient # misc imports class TrelloService: """Class to implement the business logic needed to interact with Trello""" def __init__(self) -> None: pass def get_user_oauth_token() -> GetOAuthTokenResponse: pass def get_all_boards() -> GetAllBoardsResponse: pass def get_board() -> GetBoardResponse: pass def get_all_lists() -> GetAllListsResponse: pass def get_list() -> GetListResponse: pass def get_all_labels() -> GetAllLabelsResponse: pass def get_label() -> GetLabelResponse: pass def add_card() -> AddCardResponse: pass
Not: Bu sefer testlerimizi yaptığımızda testlerimizin nasıl geçeceğine dikkat edin. Aslında bu, doğru yolda ilerlememizi sağlamamıza yardımcı olacaktır. İş akışı, işlevlerimizi genişletmek, testlerimizi yürütmek, başarılı/başarısız olup olmadığını kontrol etmek ve buna göre yeniden düzenleme yapmak olmalıdır.
__init__
işleviyle başlayalım. Buradaki fikir get_user_oauth_token
işlevini çağırmak ve TrelloClient
başlatmaktır. Yine bu tür hassas bilgilerin yalnızca .env
dosyasında saklanması gerektiğini vurgulayarak, hassas bilgileri almak için python-dotenv
bağımlılığını kullanacağız. pyproject.toml
dosyamızı buna göre değiştirdikten sonra yetkilendirme adımlarını uygulamaya başlayalım.
# trellocli/trelloservice.py class TrelloService: """Class to implement the business logic needed to interact with Trello""" def __init__(self) -> None: self.__load_oauth_token_env_var() self.__client = TrelloClient( api_key=os.getenv("TRELLO_API_KEY"), api_secret=os.getenv("TRELLO_API_SECRET"), token=os.getenv("TRELLO_OAUTH_TOKEN") ) def __load_oauth_token_env_var(self) -> None: """Private method to store user's oauth token as an environment variable""" load_dotenv() if not os.getenv("TRELLO_OAUTH_TOKEN"): res = self.get_user_oauth_token() if res.status_code == SUCCESS: dotenv_path = find_dotenv() set_key( dotenv_path=dotenv_path, key_to_set="TRELLO_OAUTH_TOKEN", value_to_set=res.token ) else: print("User denied access.") self.__load_oauth_token_env_var() def get_user_oauth_token(self) -> GetOAuthTokenResponse: """Helper method to retrieve user's oauth token Returns GetOAuthTokenResponse: user's oauth token """ try: res = create_oauth_token() return GetOAuthTokenResponse( token=res["oauth_token"], token_secret=res["oauth_token_secret"], status_code=SUCCESS ) except: return GetOAuthTokenResponse( token="", token_secret="", status_code=TRELLO_AUTHORIZATION_ERROR )
Bu uygulamada, örneğin kullanıcının yetkilendirme sırasında Deny
tıklaması gibi öngörülebilir hataların üstesinden gelmek için bir yardımcı yöntem oluşturduk. Üstelik, geçerli bir yanıt alınana kadar yinelemeli olarak kullanıcının yetkilendirmesini isteyecek şekilde ayarlanmıştır, çünkü gerçek şu ki, kullanıcı uygulamamıza kendi hesap verilerine erişme yetkisi vermedikçe devam edemeyiz.
Mücadele Köşesi 💡 TRELLO_AUTHORIZATION_ERROR
dikkat edin? Bu hatayı paket sabiti olarak bildirebilir misiniz? Daha fazla bilgi için Kurulum'a bakın
# trellocli/trelloservice.py def get_all_boards(self) -> GetAllBoardsResponse: """Method to list all boards from user's account Returns GetAllBoardsResponse: array of user's trello boards """ try: res = self.__client.list_boards() return GetAllBoardsResponse( res=res, status_code=SUCCESS ) except: return GetAllBoardsResponse( res=[], status_code=TRELLO_READ_ERROR ) def get_board(self, board_id: str) -> GetBoardResponse: """Method to retrieve board Required Args board_id (str): board id Returns GetBoardResponse: trello board """ try: res = self.__client.get_board(board_id=board_id) return GetBoardResponse( res=res, status_code=SUCCESS ) except: return GetBoardResponse( res=None, status_code=TRELLO_READ_ERROR )
Listeleri (sütunları) almaya gelince, py-trello
Board
sınıfını kontrol etmemiz gerekecek, başka bir deyişle Board
değer tipinin yeni bir parametresini kabul etmemiz gerekecek.
# trellocli/trelloservice.py def get_all_lists(self, board: Board) -> GetAllListsResponse: """Method to list all lists (columns) from the trello board Required Args board (Board): trello board Returns GetAllListsResponse: array of trello lists """ try: res = board.all_lists() return GetAllListsResponse( res=res, status_code=SUCCESS ) except: return GetAllListsResponse( res=[], status_code=TRELLO_READ_ERROR ) def get_list(self, board: Board, list_id: str) -> GetListResponse: """Method to retrieve list (column) from the trello board Required Args board (Board): trello board list_id (str): list id Returns GetListResponse: trello list """ try: res = board.get_list(list_id=list_id) return GetListResponse( res=res, status_code=SUCCESS ) except: return GetListResponse( res=None, status_code=TRELLO_READ_ERROR )
Challenge Köşesi 💡 get_all_labels
ve get_label
işlevini kendi başınıza uygulayabilir misiniz? py-trello
Board
sınıfını gözden geçirin. Uygulamamın nasıl göründüğünü görmek için başvurmaktan çekinmeyin
# trellocli/trelloservice.py def add_card( self, col: Trellolist, name: str, desc: str = "", labels: List[Label] = [] ) -> AddCardResponse: """Method to add a new card to a list (column) on the trello board Required Args col (Trellolist): trello list name (str): card name Optional Args desc (str): card description labels (List[Label]): list of labels to be added to the card Returns AddCardResponse: newly-added card """ try: # create new card new_card = col.add_card(name=name) # add optional description if desc: new_card.set_description(description=desc) # add optional labels if labels: for label in labels: new_card.add_label(label=label) return AddCardResponse( res=new_card, status_code=SUCCESS ) except: return AddCardResponse( res=new_card, status_code=TRELLO_WRITE_ERROR )
Tebrikler! Kazandın. Tekrar oynayalım mı (e/h)?