태그 보관물: python

[Tip] Emacs에서 pdb 사용할 때 UnicodeEncodeError

*** UnicodeEncodeError: ‘ascii’ codec can’t encode characters in position 168-169: ordinal not in range(128)

Emacs에서 shell을 열고 pdb를 실행 할 때 표시하고자 하는 문자열이 ASCII가 아니라면 발생 할 수 있는 문제인데 emacs의 초기화 파일에 unicode locale 설정을 해주는 것으로 해결할 수 있다. 인터넷 문서들 중에는 LANG, LC_LANG, LC_CTYPE 모두를 설정해 주어야 한다는 내용도 있었으나, 내 terminal에서 LC_CTYPE만 설정해서도 잘 동작되고 있으므로 이를 따라 LC_CTYPE만 UTF-8으로 다음과 같이 설정해 주었다.

;;; **************************************************************
;;; Unicode Encoding
;;; **************************************************************
(setenv "LC_CTYPE" "UTF-8")

Init file을 reload하거나 emac를 재 실행해서 ASCII외의 문자들이 잘 표시 되는지 확인해 본다.

그리고 환경 설정은 GitHub repository에 업뎃. 🙂

[Tip] Mac version docker에서 띄운 (웹) 서버에 접속하기

Mac version Docker에 띄워둔 web server에 host에서 접속하려면 어떻게 해야 할까? 실행할 때 ‘–network=host’를 주면 된다는 얘기가 있어서 해봤는데, Mac에서는 통하지 않았다 이건 linux용이라고… Networking features in Docker Desktop for Mac에 따라 실행할 때 port를 매핑하는 것으로 이 문제를 해결할 수 있다.

Docker의 9090 port에 Mac의 9090 port로 접근하려면 다음과 같이 -p option으로 port를 매핑해서 docker를 띄운 후 서버를 실행한다.

# Port 매핑으로 docker를 실행
MAC$ docker run --rm -ti -p 9090:9090 DOCKER_IMAGE

# Docker에서 웹서버를 띄운다. Python http.server module을 사용.
DOCKER$ python -m http.server 9090
Serving HTTP on 0.0.0.0 port 9090 (http://0.0.0.0:9090/) ...

이제 Mac의 web browser로 해당 포트에 접근할 수 있다.

App engine – 지정한 달의 google calendar event 가져오기

Google calendar API 설명문서에는 event list를 가져오는 방법이 설명과 함께 예제 code가 제시되어 있다.

page_token = None
while True:
  events = service.events().list(calendarId='primary', pageToken=page_token).execute()
  for event in events['items']:
    print event['summary']
  page_token = events.get('nextPageToken')
  if not page_token:
    break

이 코드를 수행하면 달력이 가지고 있는 모든 event들을 가져오는데, 이것을 조금 수정해서 지정된 달에 해당하는 event 목록을 가져오는 것으로 변경해 보자.

timeMax / timeMin

가져올 event의 범위는 lower bound를 나타내는 timeMin과 upper bound를 나타내는 timeMax값으로 제한할 수 있는데 이때 사용되는 날짜의 형식은 RFC3339을 따라야 한다.

calendar.monthrange() 

이 method는 년도와 월을 받아서 시작일의 주와 마지막 날짜를 tuple로 반환해 준다. 다음과 같이 특정 달의 마지막 날짜를 구할 수 있다.

>>> import calendar
>>> calendar.monthrange(2002, 06)
(5, 30)
>>> calendar.monthrange(2002, 06)[1]
30

Python에서 RFC3339 표시하기

이 형식은 간단히 말해 날짜를 나타내는 YYYY-MM-DD 형식과 시간을 나타내는 hh:mm:ss가 대문자 ‘T’로 연결되며 그 뒤에는 UTC로 부터의 time zone offset값이 붙는다.

2002-06-30T20:00:00+09:00

datetime.isoformat()을 이용하면 비슷한 형식을 얻을 수는 있으나, 뒤에 붙는 time zone offset이 표시되지 않는다.

>>> datetime(2002, 06, 30, 20, 0, 0).isoformat()
'2002-06-30T20:00:00'

Python에서 time zone을 나타내는 tzinfo class의 설명문에 따르면 tzinfo는 abstract class여서 직접 사용할 수 없고, 이를 상속받는 class를 구현해서 사용해야 한다. 다음은 이 문서에 있는 예제를 조금 수정하여 time zone offset 값을 반환하는 tzinfo의 concrete class를 구현한 것이다.

from datetime import tzinfo, timedelta, datetime

ZERO = timedelta(0)
HOUR = timedelta(hours=1)

class TimeZoneOffset(tzinfo):
    def __init__(self, hours, name):
        self.__offset = timedelta(hours = hours)
        self.__name = name

    def utcoffset(self, dt):
        return self.__offset

    def tzname(self, dt):
        return self.__name

    def dst(self, dt):
        return ZERO

Code

TimeZoneOffset class를 선언한 후에는 datetime()을 호출할때 tzinfo에 TimeZoneOffset의 instance를 넘겨서 time zone offset 정보를 포함하는 RFC3339 날짜 형식을 얻을 수 있다.

# What's the last date of the Jun, 2002?
lastDate = calendar.monthrange(2002, 6)[1]

# Lower bound RFC3339 format '2002-06-01T00:00:00+09:00'
lbound = datetime(2002, 6, 1, tzinfo=TimeZoneOffset(9, "KST")).isoformat()

# Lower bound RFC3339 format '2002-06-01T23:59:59+09:00'
ubound = datetime(2002, 6, lastDate, 23, 59, 59, tzinfo=TimeZoneOffset(9, "KST")).isoformat()

page_token = None
while True:
  # Fetch events within given lower/upper bounds.
  events = service.events().list(calendarId='primary', minTime=lbound, maxTime=ubound, pageToken=page_token).execute()
  for event in events['items']:
    print event['summary']
  page_token = events.get('nextPageToken')
  if not page_token:
    break