python pytz에 좀 버그가 있다. 



근데, 어디서 많이 본 정상혁 아저씨가 보인다

https://github.com/stub42/pytz/blob/master/tz/asia#L1928

(참고 썸머 타임 글 관련 기고 https://d2.naver.com/helloworld/645609)



>>> import pytz

>>> from datetime import datetime

>>> fmt = '%Y-%m-%d %H:%M:%S %Z%z'

>>> seoul = pytz.timezone('Asia/Seoul')

>>> seoul

<DstTzInfo 'Asia/Seoul' LMT+8:28:00 STD>

>>> seoul_dt = seoul.localize(datetime(2018, 6, 19, 17, 53))

>>> seoul_dt.strftime(fmt)

'2018-06-19 17:53:00 KST+0900'


관련해서 내용을 올렸다.


https://github.com/stub42/pytz/issues/15


Hi!

I found a time zone issue which changed Pyongyang(North Korea) time zoned recently.
According to 'https://en.wikipedia.org/wiki/Time_in_North_Korea', I found 'On 29 April 2018, North Korean leader Kim Jong-un announced his country would be returning to UTC+9 to realign its clocks with South Korea. '. It based on the Guadian Newspaper('https://www.theguardian.com/world/2018/may/05/time-for-change-north-korea-moves-clocks-forward-to-match-south')

Below code is not match the Wiki.

import pytz
import datetime

def main():
	
	seoul = pytz.timezone('Asia/Seoul')
	print(seoul.localize(datetime.datetime.now()))
	
	pyongyang = pytz.timezone('Asia/Pyongyang')
	print(pyongyang.localize(datetime.datetime.now()))
	
if __name__ == '__main__':
	main()

The result is below.

2018-06-19 18:23:36.818206+09:00
2018-06-19 18:23:36.818469+08:30

Second result should be equal to '2018-06-19 18:23:36.818469+09:00'

Could you change code and and document(https://github.com/stub42/pytz/blob/master/tz/asia#L1997)?

And When I test the previous example, I found another interesting sample code.

>>> import pytz

>>> from datetime import datetime

>>> fmt = '%Y-%m-%d %H:%M:%S %Z%z'

>>> seoul = pytz.timezone('Asia/Seoul')

>>> seoul

<DstTzInfo 'Asia/Seoul' KST+8:30:00 STD>
>>> pyongyang = pytz.timezone('Asia/Pyongyang')
>>> pyounyang

<DstTzInfo 'Asia/Pyongyang' KST+8:30:00 STD>

Finally, I found another interesting document. You described world timezone. Previsous menthioned, I think it should be changed.

https://github.com/stub42/pytz/blob/master/tz/asia#L47
https://github.com/stub42/pytz/blob/master/tz/asia#L50
-> I think it should be removed at '8:30 KST KDT Korea when at +0830', maintained at ''9:00 KST KDT Korea when at +09'.

Thanks in advance.






Posted by '김용환'
,


간단 코드 예시


import requests


def main():
print('Hello, world!')
response = requests.get('https://httpbin.org/ip')
print(response.status_code)
print(response.headers)
print('Your IP is {0}'.format(response.json()['origin']))

if __name__ == '__main__':
main()



결과는 다음과 같다. 


Hello, world!

200

{'Connection': 'keep-alive', 'Server': 'gunicorn/19.8.1', 'Date': 'Mon, 04 Jun 2018 02:28:09 GMT', 'Content-Type': 'application/json', 'Content-Length': '26', 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Credentials': 'true', 'Via': '1.1 vegur'}

Your IP is 1.1.1.1





HTTPAdapter를 이용하는 코드이다.


from requests import Session
from requests.adapters import HTTPAdapter


def main():
print('Hello, world!')

session = Session()
session.mount("http://", HTTPAdapter(max_retries=3))
response = session.get('https://httpbin.org/ip', timeout=0)

print(response.status_code)
print(response.headers)
print('Your IP is {0}'.format(response.json()['origin']))

if __name__ == '__main__':
main()




결과는 동일하다.




from requests import Session
from requests.adapters import HTTPAdapter
from requests.packages.urllib3.util.retry import Retry


def main():
print('Hello, world!')
retries_number = 3
backoff_factor = 0.3
status_forcelist = (500, 400)

retry = Retry(
total=retries_number,
read=retries_number,
connect=retries_number,
backoff_factor=backoff_factor,
status_forcelist=status_forcelist,
)
session = Session()
session.mount("http://", HTTPAdapter(max_retries=retry))
response = session.get('https://httpbin.org/ip', timeout=0)

print(response.status_code)
print(response.headers)
print('Your IP is {0}'.format(response.json()['origin']))

if __name__ == '__main__':
main()



아래 공식에 따르면, 다음과 같다.  총 소요되는 시간은 1.8인데... 


{backoff factor} * (2 ^ ({number of total retries} - 1))



0.3 * ( 2 ^ ( 1 - 1)) = 0

0.3 * ( 2 ^ ( 2 - 1)) = 0.6

0.3 * ( 2 ^ ( 3 - 1)) = 1.2



1.8 = 0 + 0.6 + 1.2






https://urllib3.readthedocs.io/en/latest/reference/urllib3.util.html#module-urllib3.util.retry


backoff_factor (float) –

A backoff factor to apply between attempts after the second try (most errors are resolved immediately by a second try without a delay). urllib3 will sleep for:

{backoff factor} * (2 ^ ({number of total retries} - 1))

seconds. If the backoff_factor is 0.1, then sleep() will sleep for [0.0s, 0.2s, 0.4s, …] between retries. It will never be longer than Retry.BACKOFF_MAX.

By default, backoff is disabled (set to 0).






만약 타임아웃이 생기면, 중간에 쉬는 타임이 생긴다. retries와 timeout을 잘 사용하면 괜찮을 것 같다. 


response = session.get('https://httpbin.org/ip', timeout=5)

Posted by '김용환'
,


python에 특이한 문법인 try-else문이 있어서 살펴본 예이다. 


except문이 실행되지 않으면 else 문이 실행된다. 

a=0
try:
a=1
except ZeroDivisionError as e:
print(str(e))
else:
print(a)


결과는 1이다.





다음은 일부러 0으로 나눠 ZeroDivisionError를 발생시키는 코드이다. 


except 문이 실행되면 else문이 실행되지 않는다. 

a=0
try:
a = 4/0
except ZeroDivisionError as e:
print(str(e))
else:
print(a)


결과는 다음과 같다.


division by zero




Posted by '김용환'
,


python에 특이한 문법인 for else가 있다고 해서 살펴봤다. 


for - else를 실행하는 예이다.


data = [1, 2, 3, 4, 5]
for i in data:
print(i)
else:
print("aa")

print("end")


결과는 다음과 같다.


1

2

3

4

5

aa

end






왜 필요할까. break와 연관된 문법이다.



중간에 break 문을 만나 for 문 바깥으로 나가면 else 문이 출력되지 않는다. 


data = [1, 2, 3, 4, 5]
for i in data:
print(i)
if i == 3:
break
else:
print("aa")

print("end")




결과는 다음과 같다.




1

2

3

end





Posted by '김용환'
,

zookeeper와 연동하는 kazoo를 python3로 업그레이드하면서 알게된 내용이다.




python2에서는 바이트 문자열(byte string)이라는 것은 무시되었다.



A prefix of 'b' or 'B' is ignored in Python 2; it indicates that the literal should become a bytes literal in Python 3 (e.g. when code is automatically converted with 2to3). A 'u' or 'b' prefix may be followed by an 'r' prefix.



그러나, python3부터는 바이트 문자열을 b또는 B로 쓰이게 되었다.


Bytes literals are always prefixed with 'b' or 'B'; they produce an instance of the bytes type instead of the str type. They may only contain ASCII characters; bytes with a numeric value of 128 or greater must be expressed with escapes.





즉 저장할 때 문자열은 encode()로,


 value.encode()


읽을 때는 decode()로 읽는다.


value.decode()






Posted by '김용환'
,



python3에서 


jinja2.exceptions.UndefinedError: 'len' is undefined 해결하려면


'|length'를 이용한다.


{% node.data|length == 0 %}


Posted by '김용환'
,


flask의 테스트 코드를 실행할 때 사용되는 툴은 다음과 같다.


$ tox -e flake8,py27



tox는 표준 툴이다.

https://tox.readthedocs.io/en/latest/




pyenv를 사용하고 있다면 다음과 같이 설치후 사용할 수 있다.


pip install -r requirements.txt -i http://proxy.google.com/pypi/simple/ --trusted-host proxy.google.com

~/.pyenv/shims/tox -e flake8,py27


Posted by '김용환'
,


아래와 같이 select의 컬럼과 from의 테이블이 서로 다르다.(사실 이게 되기도 한다)


SELECT distinct kibanaauth_esidx.esidx

FROM kibanaauth_role role



이전 쿼리는 kibanaauth_role와 kibanaauth_esidx가 다르기 때문에 조인을 할 수 없다.


sql_alchemy의 query()를 join()과 함께 쓸 때는 

내부적으로 SQL의 select와 from 뒤에 query() 매개 변수에 포함되는 모델의 테이블을 무조건 적용하게 된다.







그래서 아래와 같이 select와 from을 동일한 테이블이 나오도록 쿼리를 수정한 후,, 


SELECT DISTINCT kibanaauth_esidx.esidx AS kibanaauth_esidx_esidx

FROM kibanaauth_esidx 



아래와 같이 sql_alchemy 문을 만들어서 테스트해보니. 조인이 된다.


aaa = session.query(LogAuthServiceTag.esidx).distinct() \

                    .join(LogAuthRoleServiceTag, LogAuthServiceTag.id == LogAuthRoleServiceTag.esidx_id) \

                    .join(LogAuthRole, LogAuthRole.id == LogAuthRoleServiceTag.role_id) \

                    .join(LogAuthRoleUser, LogAuthRoleUser.role_id == LogAuthRole.id) \

                    .join(LogAuthUser, LogAuthUser.id == LogAuthRoleUser.user_id) \

                    .filter(LogAuthUser.userid == userid)



query() 문에 여러 모델을 넣어도 sql_alchemy가 내부적으로 조합하기 때문에 

상황에 따라서는 from이 이상하게 나올 수 있다. 


복잡하게 sql_alchemy 를 사용할 때는 SQL 문장을 디버깅하면서 확인해야 한다.





다시 얘기하면. 



session.query(Post) \

       .join(User, Post.author_id == User.id)



이 문장은 다음과 같이 변환될 것이다.  query()의 매개 변수는 select, from으로 넘어갔다(항상 그런 것은 아니지만, 대개 그렇다.)


select post

from post 

inner join user 

   on post.author_id == user.id




Posted by '김용환'
,



python의 sql_alchemy에서 3개의 테이블을 조인하고 특정 사람의 권한을 보고 싶은 쿼리가 있다.



select role.role

from 

(user join roleuser on user.id = roleuser.user_id)

left join role role on roleuser.role_id = role.id

where user.userid = 'sma'



python의 sql_alchemy는 다음과 같이 코딩한다.



                instance = session.query(Role.role)\

                    .join(RoleUser, User.id == RoleUser.user_id) \

                    .outerjoin(Role, RoleUser.role_id == Role.id) \

                    .filter(User.userid == userid)



조인된 다른 테이블을 보려면 다음과 같다.


                instance = session.query(Role.role)\

                    .join(RoleUser, User.id == RoleUser.user_id) \

                    .outerjoin(Role, RoleUser.role_id == Role.id) \

                    .filter(User.userid == userid) \

                   .add_entity(RoleUser) \

                   .add_entity(User) \

Posted by '김용환'
,



파이썬의 sql_alchemy에서 SQL을 디버깅하려면 다음과 같이 str을 사용해야 한다..



                instance = session.query(...) \

                    .filter(...) \

                    .outerjoin((..))


                print(str(instance))

                

Posted by '김용환'
,